Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/GPU/Vulkan/DrawEngineVulkan.cpp
3186 views
1
// Copyright (c) 2012- PPSSPP Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
#include "ppsspp_config.h"
19
#include <functional>
20
21
#include "Common/Profiler/Profiler.h"
22
#include "Common/GPU/Vulkan/VulkanRenderManager.h"
23
24
#include "Common/Log.h"
25
26
#include "GPU/GPUState.h"
27
#include "GPU/ge_constants.h"
28
29
#include "Common/GPU/Vulkan/VulkanContext.h"
30
#include "Common/GPU/Vulkan/VulkanMemory.h"
31
32
#include "GPU/Common/SplineCommon.h"
33
#include "GPU/Common/TransformCommon.h"
34
#include "GPU/Common/VertexDecoderCommon.h"
35
#include "GPU/Common/SoftwareTransformCommon.h"
36
#include "GPU/Common/DrawEngineCommon.h"
37
#include "GPU/Common/ShaderUniforms.h"
38
#include "GPU/Vulkan/DrawEngineVulkan.h"
39
#include "GPU/Vulkan/TextureCacheVulkan.h"
40
#include "GPU/Vulkan/ShaderManagerVulkan.h"
41
#include "GPU/Vulkan/PipelineManagerVulkan.h"
42
#include "GPU/Vulkan/FramebufferManagerVulkan.h"
43
44
using namespace PPSSPP_VK;
45
46
enum {
47
TRANSFORMED_VERTEX_BUFFER_SIZE = VERTEX_BUFFER_MAX * sizeof(TransformedVertex)
48
};
49
50
DrawEngineVulkan::DrawEngineVulkan(Draw::DrawContext *draw)
51
: draw_(draw) {
52
decOptions_.expandAllWeightsToFloat = false;
53
decOptions_.expand8BitNormalsToFloat = false;
54
}
55
56
void DrawEngineVulkan::InitDeviceObjects() {
57
// All resources we need for PSP drawing. Usually only bindings 0 and 2-4 are populated.
58
59
BindingType bindingTypes[VKRPipelineLayout::MAX_DESC_SET_BINDINGS] = {
60
BindingType::COMBINED_IMAGE_SAMPLER, // main
61
BindingType::COMBINED_IMAGE_SAMPLER, // framebuffer-read
62
BindingType::COMBINED_IMAGE_SAMPLER, // palette
63
BindingType::UNIFORM_BUFFER_DYNAMIC_ALL, // uniforms
64
BindingType::UNIFORM_BUFFER_DYNAMIC_VERTEX, // lights
65
BindingType::UNIFORM_BUFFER_DYNAMIC_VERTEX, // bones
66
BindingType::STORAGE_BUFFER_VERTEX, // tess
67
BindingType::STORAGE_BUFFER_VERTEX,
68
BindingType::STORAGE_BUFFER_VERTEX,
69
};
70
71
VulkanContext *vulkan = (VulkanContext *)draw_->GetNativeObject(Draw::NativeObject::CONTEXT);
72
VkDevice device = vulkan->GetDevice();
73
74
VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
75
pipelineLayout_ = renderManager->CreatePipelineLayout(bindingTypes, ARRAY_SIZE(bindingTypes), draw_->GetDeviceCaps().geometryShaderSupported, "drawengine_layout");
76
77
pushUBO_ = (VulkanPushPool *)draw_->GetNativeObject(Draw::NativeObject::PUSH_POOL);
78
pushVertex_ = new VulkanPushPool(vulkan, "pushVertex", 4 * 1024 * 1024, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
79
pushIndex_ = new VulkanPushPool(vulkan, "pushIndex", 1 * 512 * 1024, VK_BUFFER_USAGE_INDEX_BUFFER_BIT);
80
81
VkSamplerCreateInfo samp{ VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
82
samp.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
83
samp.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
84
samp.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
85
samp.magFilter = VK_FILTER_LINEAR;
86
samp.minFilter = VK_FILTER_LINEAR;
87
samp.maxLod = VK_LOD_CLAMP_NONE; // recommended by best practices, has no effect since we don't use mipmaps.
88
VkResult res = vkCreateSampler(device, &samp, nullptr, &samplerSecondaryLinear_);
89
samp.magFilter = VK_FILTER_NEAREST;
90
samp.minFilter = VK_FILTER_NEAREST;
91
res = vkCreateSampler(device, &samp, nullptr, &samplerSecondaryNearest_);
92
_dbg_assert_(VK_SUCCESS == res);
93
res = vkCreateSampler(device, &samp, nullptr, &nullSampler_);
94
_dbg_assert_(VK_SUCCESS == res);
95
96
tessDataTransferVulkan = new TessellationDataTransferVulkan(vulkan);
97
tessDataTransfer = tessDataTransferVulkan;
98
99
draw_->SetInvalidationCallback(std::bind(&DrawEngineVulkan::Invalidate, this, std::placeholders::_1));
100
}
101
102
DrawEngineVulkan::~DrawEngineVulkan() {
103
DestroyDeviceObjects();
104
}
105
106
void DrawEngineVulkan::DestroyDeviceObjects() {
107
if (!draw_) {
108
// We've already done this from LostDevice.
109
return;
110
}
111
112
VulkanContext *vulkan = (VulkanContext *)draw_->GetNativeObject(Draw::NativeObject::CONTEXT);
113
VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
114
115
draw_->SetInvalidationCallback(InvalidationCallback());
116
117
delete tessDataTransferVulkan;
118
tessDataTransfer = nullptr;
119
tessDataTransferVulkan = nullptr;
120
121
pushUBO_ = nullptr;
122
123
if (pushVertex_) {
124
pushVertex_->Destroy();
125
delete pushVertex_;
126
pushVertex_ = nullptr;
127
}
128
if (pushIndex_) {
129
pushIndex_->Destroy();
130
delete pushIndex_;
131
pushIndex_ = nullptr;
132
}
133
134
if (samplerSecondaryNearest_ != VK_NULL_HANDLE)
135
vulkan->Delete().QueueDeleteSampler(samplerSecondaryNearest_);
136
if (samplerSecondaryLinear_ != VK_NULL_HANDLE)
137
vulkan->Delete().QueueDeleteSampler(samplerSecondaryLinear_);
138
if (nullSampler_ != VK_NULL_HANDLE)
139
vulkan->Delete().QueueDeleteSampler(nullSampler_);
140
141
renderManager->DestroyPipelineLayout(pipelineLayout_);
142
}
143
144
void DrawEngineVulkan::DeviceLost() {
145
DestroyDeviceObjects();
146
DirtyAllUBOs();
147
draw_ = nullptr;
148
}
149
150
void DrawEngineVulkan::DeviceRestore(Draw::DrawContext *draw) {
151
draw_ = draw;
152
InitDeviceObjects();
153
}
154
155
void DrawEngineVulkan::BeginFrame() {
156
DrawEngineCommon::BeginFrame();
157
158
lastPipeline_ = nullptr;
159
160
// These will be re-bound if needed, let's not let old bindings linger around too long.
161
boundDepal_ = VK_NULL_HANDLE;
162
boundSecondary_ = VK_NULL_HANDLE;
163
164
// pushUBO is the thin3d push pool, don't need to BeginFrame again.
165
pushVertex_->BeginFrame();
166
pushIndex_->BeginFrame();
167
168
tessDataTransferVulkan->SetPushPool(pushUBO_);
169
170
DirtyAllUBOs();
171
172
AssertEmpty();
173
}
174
175
void DrawEngineVulkan::EndFrame() {
176
stats_.pushVertexSpaceUsed = (int)pushVertex_->GetUsedThisFrame();
177
stats_.pushIndexSpaceUsed = (int)pushIndex_->GetUsedThisFrame();
178
179
AssertEmpty();
180
}
181
182
void DrawEngineVulkan::DirtyAllUBOs() {
183
baseUBOOffset = 0;
184
lightUBOOffset = 0;
185
boneUBOOffset = 0;
186
baseBuf = VK_NULL_HANDLE;
187
lightBuf = VK_NULL_HANDLE;
188
boneBuf = VK_NULL_HANDLE;
189
dirtyUniforms_ = DIRTY_BASE_UNIFORMS | DIRTY_LIGHT_UNIFORMS | DIRTY_BONE_UNIFORMS;
190
imageView = VK_NULL_HANDLE;
191
sampler = VK_NULL_HANDLE;
192
gstate_c.Dirty(DIRTY_TEXTURE_IMAGE);
193
}
194
195
void DrawEngineVulkan::Invalidate(InvalidationCallbackFlags flags) {
196
if (flags & InvalidationCallbackFlags::COMMAND_BUFFER_STATE) {
197
// Nothing here anymore (removed the "frame descriptor set"
198
// If we add back "seldomly-changing" descriptors, we might use this again.
199
}
200
if (flags & InvalidationCallbackFlags::RENDER_PASS_STATE) {
201
// If have a new render pass, dirty our dynamic state so it gets re-set.
202
//
203
// Dirty everything that has dynamic state that will need re-recording.
204
gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_BLEND_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS);
205
lastPipeline_ = nullptr;
206
}
207
}
208
209
// The inline wrapper in the header checks for numDrawCalls_ == 0
210
void DrawEngineVulkan::Flush() {
211
if (!numDrawVerts_) {
212
return;
213
}
214
215
VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
216
217
renderManager->AssertInRenderPass();
218
219
PROFILE_THIS_SCOPE("Flush");
220
221
bool tess = gstate_c.submitType == SubmitType::HW_BEZIER || gstate_c.submitType == SubmitType::HW_SPLINE;
222
223
bool textureNeedsApply = false;
224
if (gstate_c.IsDirty(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS) && !gstate.isModeClear() && gstate.isTextureMapEnabled()) {
225
textureCache_->SetTexture();
226
gstate_c.Clean(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS);
227
// NOTE: After this is set, we MUST call ApplyTexture before returning.
228
textureNeedsApply = true;
229
} else if (gstate.getTextureAddress(0) == (gstate.getFrameBufRawAddress() | 0x04000000)) {
230
// This catches the case of clearing a texture.
231
gstate_c.Dirty(DIRTY_TEXTURE_IMAGE);
232
}
233
234
GEPrimitiveType prim = prevPrim_;
235
236
// Always use software for flat shading to fix the provoking index
237
// if the provoking vertex extension is not available.
238
bool provokingVertexOk = (tess || gstate.getShadeMode() != GE_SHADE_FLAT);
239
if (renderManager->GetVulkanContext()->GetDeviceFeatures().enabled.provokingVertex.provokingVertexLast) {
240
provokingVertexOk = true;
241
}
242
bool useHWTransform = CanUseHardwareTransform(prim) && provokingVertexOk;
243
244
// The optimization to avoid indexing isn't really worth it on Vulkan since it means creating more pipelines.
245
// This could be avoided with the new dynamic state extensions, but not available enough on mobile.
246
const bool forceIndexed = draw_->GetDeviceCaps().verySlowShaderCompiler;
247
248
if (useHWTransform) {
249
uint32_t vbOffset;
250
251
VkBuffer vbuf = VK_NULL_HANDLE;
252
if (applySkinInDecode_ && (lastVType_ & GE_VTYPE_WEIGHT_MASK)) {
253
// If software skinning, we're predecoding into "decoded". So make sure we're done, then push that content.
254
DecodeVerts(dec_, decoded_);
255
VkDeviceSize size = numDecodedVerts_ * dec_->GetDecVtxFmt().stride;
256
u8 *dest = (u8 *)pushVertex_->Allocate(size, 4, &vbuf, &vbOffset);
257
memcpy(dest, decoded_, size);
258
} else {
259
// Figure out how much pushbuffer space we need to allocate.
260
int vertsToDecode = ComputeNumVertsToDecode();
261
// Decode directly into the pushbuffer
262
u8 *dest = pushVertex_->Allocate(vertsToDecode * dec_->GetDecVtxFmt().stride, 4, &vbuf, &vbOffset);
263
DecodeVerts(dec_, dest);
264
}
265
266
int vertexCount;
267
int maxIndex;
268
bool useElements;
269
DecodeIndsAndGetData(&prim, &vertexCount, &maxIndex, &useElements, false);
270
271
bool hasColor = (lastVType_ & GE_VTYPE_COL_MASK) != GE_VTYPE_COL_NONE;
272
if (gstate.isModeThrough()) {
273
gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && (hasColor || gstate.getMaterialAmbientA() == 255);
274
} else {
275
gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && ((hasColor && (gstate.materialupdate & 1)) || gstate.getMaterialAmbientA() == 255) && (!gstate.isLightingEnabled() || gstate.getAmbientA() == 255);
276
}
277
278
if (textureNeedsApply) {
279
textureCache_->ApplyTexture();
280
textureCache_->GetVulkanHandles(imageView, sampler);
281
if (imageView == VK_NULL_HANDLE)
282
imageView = (VkImageView)draw_->GetNativeObject(gstate_c.textureIsArray ? Draw::NativeObject::NULL_IMAGEVIEW_ARRAY : Draw::NativeObject::NULL_IMAGEVIEW);
283
if (sampler == VK_NULL_HANDLE)
284
sampler = nullSampler_;
285
}
286
287
if (!lastPipeline_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE) || prim != lastPrim_) {
288
if (prim != lastPrim_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE)) {
289
ConvertStateToVulkanKey(*framebufferManager_, shaderManager_, prim, pipelineKey_, dynState_);
290
}
291
292
VulkanVertexShader *vshader = nullptr;
293
VulkanFragmentShader *fshader = nullptr;
294
VulkanGeometryShader *gshader = nullptr;
295
296
shaderManager_->GetShaders(prim, dec_->VertexType(), &vshader, &fshader, &gshader, pipelineState_, true, useHWTessellation_, decOptions_.expandAllWeightsToFloat, applySkinInDecode_);
297
_dbg_assert_msg_(vshader->UseHWTransform(), "Bad vshader");
298
VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(renderManager, pipelineLayout_, pipelineKey_, &dec_->decFmt, vshader, fshader, gshader, true, 0, framebufferManager_->GetMSAALevel(), false);
299
if (!pipeline || !pipeline->pipeline) {
300
// Already logged, let's bail out.
301
ResetAfterDraw();
302
return;
303
}
304
BindShaderBlendTex(); // This might cause copies so important to do before BindPipeline.
305
306
if (!renderManager->BindPipeline(pipeline->pipeline, pipeline->pipelineFlags, pipelineLayout_)) {
307
renderManager->ReportBadStateForDraw();
308
ResetAfterDraw();
309
return;
310
}
311
if (pipeline != lastPipeline_) {
312
if (lastPipeline_ && !(lastPipeline_->UsesBlendConstant() && pipeline->UsesBlendConstant())) {
313
gstate_c.Dirty(DIRTY_BLEND_STATE);
314
}
315
lastPipeline_ = pipeline;
316
}
317
ApplyDrawStateLate(renderManager, false, 0, pipeline->UsesBlendConstant());
318
gstate_c.Clean(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE);
319
gstate_c.Dirty(dirtyRequiresRecheck_);
320
dirtyRequiresRecheck_ = 0;
321
lastPipeline_ = pipeline;
322
}
323
lastPrim_ = prim;
324
325
dirtyUniforms_ |= shaderManager_->UpdateUniforms(framebufferManager_->UseBufferedRendering());
326
UpdateUBOs();
327
328
int descCount = 6;
329
if (tess)
330
descCount = 9;
331
int descSetIndex;
332
PackedDescriptor *descriptors = renderManager->PushDescriptorSet(descCount, &descSetIndex);
333
descriptors[0].image.view = imageView;
334
descriptors[0].image.sampler = sampler;
335
336
descriptors[1].image.view = boundSecondary_;
337
descriptors[1].image.sampler = samplerSecondaryNearest_;
338
339
descriptors[2].image.view = boundDepal_;
340
descriptors[2].image.sampler = (boundDepal_ && boundDepalSmoothed_) ? samplerSecondaryLinear_ : samplerSecondaryNearest_;
341
342
descriptors[3].buffer.buffer = baseBuf;
343
descriptors[3].buffer.range = sizeof(UB_VS_FS_Base);
344
descriptors[3].buffer.offset = 0;
345
346
descriptors[4].buffer.buffer = lightBuf;
347
descriptors[4].buffer.range = sizeof(UB_VS_Lights);
348
descriptors[4].buffer.offset = 0;
349
350
descriptors[5].buffer.buffer = boneBuf;
351
descriptors[5].buffer.range = sizeof(UB_VS_Bones);
352
descriptors[5].buffer.offset = 0;
353
if (tess) {
354
const VkDescriptorBufferInfo *bufInfo = tessDataTransferVulkan->GetBufferInfo();
355
for (int j = 0; j < 3; j++) {
356
descriptors[j + 6].buffer.buffer = bufInfo[j].buffer;
357
descriptors[j + 6].buffer.range = bufInfo[j].range;
358
descriptors[j + 6].buffer.offset = bufInfo[j].offset;
359
}
360
}
361
// TODO: Can we avoid binding all three when not needed? Same below for hardware transform.
362
// Think this will require different descriptor set layouts.
363
const uint32_t dynamicUBOOffsets[3] = {
364
baseUBOOffset, lightUBOOffset, boneUBOOffset,
365
};
366
if (useElements) {
367
VkBuffer ibuf;
368
u32 ibOffset = (uint32_t)pushIndex_->Push(decIndex_, sizeof(uint16_t) * vertexCount, 4, &ibuf);
369
renderManager->DrawIndexed(descSetIndex, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, vertexCount, 1);
370
} else {
371
renderManager->Draw(descSetIndex, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, vertexCount);
372
}
373
if (useDepthRaster_) {
374
DepthRasterSubmitRaw(prim, dec_, dec_->VertexType(), vertexCount);
375
}
376
} else {
377
PROFILE_THIS_SCOPE("soft");
378
const VertexDecoder *swDec = dec_;
379
if (swDec->nweights != 0) {
380
u32 withSkinning = lastVType_ | (1 << 26);
381
if (withSkinning != lastVType_) {
382
swDec = GetVertexDecoder(withSkinning);
383
}
384
}
385
int prevDecodedVerts = numDecodedVerts_;
386
387
DecodeVerts(swDec, decoded_);
388
int vertexCount = DecodeInds();
389
390
bool hasColor = (lastVType_ & GE_VTYPE_COL_MASK) != GE_VTYPE_COL_NONE;
391
if (gstate.isModeThrough()) {
392
gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && (hasColor || gstate.getMaterialAmbientA() == 255);
393
} else {
394
gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && ((hasColor && (gstate.materialupdate & 1)) || gstate.getMaterialAmbientA() == 255) && (!gstate.isLightingEnabled() || gstate.getAmbientA() == 255);
395
}
396
397
gpuStats.numUncachedVertsDrawn += vertexCount;
398
prim = IndexGenerator::GeneralPrim((GEPrimitiveType)drawInds_[0].prim);
399
400
// At this point, the output is always an index triangle/line/point list, no strips/fans.
401
402
u16 *inds = decIndex_;
403
SoftwareTransformResult result{};
404
SoftwareTransformParams params{};
405
params.decoded = decoded_;
406
params.transformed = transformed_;
407
params.transformedExpanded = transformedExpanded_;
408
params.fbman = framebufferManager_;
409
params.texCache = textureCache_;
410
// In Vulkan, we have to force drawing of primitives if !framebufferManager_->UseBufferedRendering() because Vulkan clears
411
// do not respect scissor rects.
412
params.allowClear = framebufferManager_->UseBufferedRendering();
413
params.allowSeparateAlphaClear = false;
414
415
if (gstate.getShadeMode() == GE_SHADE_FLAT) {
416
if (!renderManager->GetVulkanContext()->GetDeviceFeatures().enabled.provokingVertex.provokingVertexLast) {
417
// If we can't have the hardware do it, we need to rotate the index buffer to simulate a different provoking vertex.
418
// We do this before line expansion etc.
419
IndexBufferProvokingLastToFirst(prim, inds, vertexCount);
420
}
421
}
422
params.flippedY = true;
423
params.usesHalfZ = true;
424
425
// We need to update the viewport early because it's checked for flipping in SoftwareTransform.
426
// We don't have a "DrawStateEarly" in vulkan, so...
427
// TODO: Probably should eventually refactor this and feed the vp size into SoftwareTransform directly (Unknown's idea).
428
if (gstate_c.IsDirty(DIRTY_VIEWPORTSCISSOR_STATE)) {
429
ViewportAndScissor vpAndScissor;
430
ConvertViewportAndScissor(framebufferManager_->UseBufferedRendering(),
431
framebufferManager_->GetRenderWidth(), framebufferManager_->GetRenderHeight(),
432
framebufferManager_->GetTargetBufferWidth(), framebufferManager_->GetTargetBufferHeight(),
433
vpAndScissor);
434
UpdateCachedViewportState(vpAndScissor);
435
}
436
437
// At this point, rect and line primitives are still preserved as such. So, it's the best time to do software depth raster.
438
// We could piggyback on the viewport transform below, but it gets complicated since it's different per-backend. Which we really
439
// should clean up one day...
440
if (useDepthRaster_) {
441
DepthRasterPredecoded(prim, decoded_, numDecodedVerts_, swDec, vertexCount);
442
}
443
444
SoftwareTransform swTransform(params);
445
446
const Lin::Vec3 trans(gstate_c.vpXOffset, gstate_c.vpYOffset, gstate_c.vpZOffset * 0.5f + 0.5f);
447
const Lin::Vec3 scale(gstate_c.vpWidthScale, gstate_c.vpHeightScale, gstate_c.vpDepthScale * 0.5f);
448
swTransform.SetProjMatrix(gstate.projMatrix, gstate_c.vpWidth < 0, gstate_c.vpHeight < 0, trans, scale);
449
450
swTransform.Transform(prim, swDec->VertexType(), swDec->GetDecVtxFmt(), numDecodedVerts_, &result);
451
452
// Non-zero depth clears are unusual, but some drivers don't match drawn depth values to cleared values.
453
// Games sometimes expect exact matches (see #12626, for example) for equal comparisons.
454
if (result.action == SW_CLEAR && everUsedEqualDepth_ && gstate.isClearModeDepthMask() && result.depth > 0.0f && result.depth < 1.0f)
455
result.action = SW_NOT_READY;
456
457
if (result.action == SW_NOT_READY) {
458
// decIndex_ here is always equal to inds currently, but it may not be in the future.
459
swTransform.BuildDrawingParams(prim, vertexCount, swDec->VertexType(), inds, RemainingIndices(inds), numDecodedVerts_, VERTEX_BUFFER_MAX, &result);
460
}
461
462
if (result.setSafeSize)
463
framebufferManager_->SetSafeSize(result.safeWidth, result.safeHeight);
464
465
// Only here, where we know whether to clear or to draw primitives, should we actually set the current framebuffer! Because that gives use the opportunity
466
// to use a "pre-clear" render pass, for high efficiency on tilers.
467
if (result.action == SW_DRAW_INDEXED) {
468
if (textureNeedsApply) {
469
gstate_c.pixelMapped = result.pixelMapped;
470
textureCache_->ApplyTexture();
471
gstate_c.pixelMapped = false;
472
textureCache_->GetVulkanHandles(imageView, sampler);
473
if (imageView == VK_NULL_HANDLE)
474
imageView = (VkImageView)draw_->GetNativeObject(gstate_c.textureIsArray ? Draw::NativeObject::NULL_IMAGEVIEW_ARRAY : Draw::NativeObject::NULL_IMAGEVIEW);
475
if (sampler == VK_NULL_HANDLE)
476
sampler = nullSampler_;
477
}
478
if (!lastPipeline_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE) || prim != lastPrim_) {
479
if (prim != lastPrim_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE)) {
480
ConvertStateToVulkanKey(*framebufferManager_, shaderManager_, prim, pipelineKey_, dynState_);
481
}
482
483
VulkanVertexShader *vshader = nullptr;
484
VulkanFragmentShader *fshader = nullptr;
485
VulkanGeometryShader *gshader = nullptr;
486
487
shaderManager_->GetShaders(prim, swDec->VertexType(), &vshader, &fshader, &gshader, pipelineState_, false, false, decOptions_.expandAllWeightsToFloat, true);
488
_dbg_assert_msg_(!vshader->UseHWTransform(), "Bad vshader");
489
VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(renderManager, pipelineLayout_, pipelineKey_, &swDec->decFmt, vshader, fshader, gshader, false, 0, framebufferManager_->GetMSAALevel(), false);
490
if (!pipeline || !pipeline->pipeline) {
491
// Already logged, let's bail out.
492
ResetAfterDraw();
493
return;
494
}
495
BindShaderBlendTex(); // This might cause copies so super important to do before BindPipeline.
496
497
if (!renderManager->BindPipeline(pipeline->pipeline, pipeline->pipelineFlags, pipelineLayout_)) {
498
renderManager->ReportBadStateForDraw();
499
ResetAfterDraw();
500
return;
501
}
502
if (pipeline != lastPipeline_) {
503
if (lastPipeline_ && !lastPipeline_->UsesBlendConstant() && pipeline->UsesBlendConstant()) {
504
gstate_c.Dirty(DIRTY_BLEND_STATE);
505
}
506
lastPipeline_ = pipeline;
507
}
508
ApplyDrawStateLate(renderManager, result.setStencil, result.stencilValue, pipeline->UsesBlendConstant());
509
gstate_c.Clean(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE);
510
gstate_c.Dirty(dirtyRequiresRecheck_);
511
dirtyRequiresRecheck_ = 0;
512
lastPipeline_ = pipeline;
513
}
514
515
lastPrim_ = prim;
516
517
dirtyUniforms_ |= shaderManager_->UpdateUniforms(framebufferManager_->UseBufferedRendering());
518
519
// Even if the first draw is through-mode, make sure we at least have one copy of these uniforms buffered
520
UpdateUBOs();
521
522
int descCount = 6;
523
int descSetIndex;
524
PackedDescriptor *descriptors = renderManager->PushDescriptorSet(descCount, &descSetIndex);
525
descriptors[0].image.view = imageView;
526
descriptors[0].image.sampler = sampler;
527
descriptors[1].image.view = boundSecondary_;
528
descriptors[1].image.sampler = samplerSecondaryNearest_;
529
descriptors[2].image.view = boundDepal_;
530
descriptors[2].image.sampler = (boundDepal_ && boundDepalSmoothed_) ? samplerSecondaryLinear_ : samplerSecondaryNearest_;
531
descriptors[3].buffer.buffer = baseBuf;
532
descriptors[3].buffer.range = sizeof(UB_VS_FS_Base);
533
descriptors[4].buffer.buffer = lightBuf;
534
descriptors[4].buffer.range = sizeof(UB_VS_Lights);
535
descriptors[5].buffer.buffer = boneBuf;
536
descriptors[5].buffer.range = sizeof(UB_VS_Bones);
537
538
const uint32_t dynamicUBOOffsets[3] = {
539
baseUBOOffset, lightUBOOffset, boneUBOOffset,
540
};
541
542
PROFILE_THIS_SCOPE("renderman_q");
543
544
VkBuffer vbuf, ibuf;
545
u32 vbOffset = (uint32_t)pushVertex_->Push(result.drawBuffer, numDecodedVerts_ * sizeof(TransformedVertex), 4, &vbuf);
546
u32 ibOffset = (uint32_t)pushIndex_->Push(inds, sizeof(short) * result.drawNumTrans, 4, &ibuf);
547
renderManager->DrawIndexed(descSetIndex, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, result.drawNumTrans, 1);
548
} else if (result.action == SW_CLEAR) {
549
// Note: we won't get here if the clear is alpha but not color, or color but not alpha.
550
bool clearColor = gstate.isClearModeColorMask();
551
bool clearAlpha = gstate.isClearModeAlphaMask(); // and stencil
552
bool clearDepth = gstate.isClearModeDepthMask();
553
Draw::Aspect mask = Draw::Aspect::NO_BIT;
554
// The Clear detection takes care of doing a regular draw instead if separate masking
555
// of color and alpha is needed, so we can just treat them as the same.
556
if (clearColor || clearAlpha) mask |= Draw::Aspect::COLOR_BIT;
557
if (clearDepth) mask |= Draw::Aspect::DEPTH_BIT;
558
if (clearAlpha) mask |= Draw::Aspect::STENCIL_BIT;
559
// Note that since the alpha channel and the stencil channel are shared on the PSP,
560
// when we clear alpha, we also clear stencil to the same value.
561
draw_->Clear(mask, result.color, result.depth, result.color >> 24);
562
if (gstate_c.Use(GPU_USE_CLEAR_RAM_HACK) && gstate.isClearModeColorMask() && (gstate.isClearModeAlphaMask() || gstate.FrameBufFormat() == GE_FORMAT_565)) {
563
int scissorX1 = gstate.getScissorX1();
564
int scissorY1 = gstate.getScissorY1();
565
int scissorX2 = gstate.getScissorX2() + 1;
566
int scissorY2 = gstate.getScissorY2() + 1;
567
framebufferManager_->ApplyClearToMemory(scissorX1, scissorY1, scissorX2, scissorY2, result.color);
568
}
569
}
570
}
571
572
ResetAfterDrawInline();
573
574
framebufferManager_->SetColorUpdated(gstate_c.skipDrawReason);
575
576
gpuCommon_->NotifyFlush();
577
}
578
579
void DrawEngineVulkan::ResetAfterDraw() {
580
indexGen.Reset();
581
numDecodedVerts_ = 0;
582
numDrawVerts_ = 0;
583
numDrawInds_ = 0;
584
vertexCountInDrawCalls_ = 0;
585
decodeIndsCounter_ = 0;
586
decodeVertsCounter_ = 0;
587
gstate_c.vertexFullAlpha = true;
588
}
589
590
void DrawEngineVulkan::UpdateUBOs() {
591
if ((dirtyUniforms_ & DIRTY_BASE_UNIFORMS) || baseBuf == VK_NULL_HANDLE) {
592
baseUBOOffset = shaderManager_->PushBaseBuffer(pushUBO_, &baseBuf);
593
dirtyUniforms_ &= ~DIRTY_BASE_UNIFORMS;
594
}
595
if ((dirtyUniforms_ & DIRTY_LIGHT_UNIFORMS) || lightBuf == VK_NULL_HANDLE) {
596
lightUBOOffset = shaderManager_->PushLightBuffer(pushUBO_, &lightBuf);
597
dirtyUniforms_ &= ~DIRTY_LIGHT_UNIFORMS;
598
}
599
if ((dirtyUniforms_ & DIRTY_BONE_UNIFORMS) || boneBuf == VK_NULL_HANDLE) {
600
boneUBOOffset = shaderManager_->PushBoneBuffer(pushUBO_, &boneBuf);
601
dirtyUniforms_ &= ~DIRTY_BONE_UNIFORMS;
602
}
603
}
604
605
void TessellationDataTransferVulkan::SendDataToShader(const SimpleVertex *const *points, int size_u, int size_v, u32 vertType, const Spline::Weight2D &weights) {
606
// SSBOs that are not simply float1 or float2 need to be padded up to a float4 size. vec3 members
607
// also need to be 16-byte aligned, hence the padding.
608
struct TessData {
609
float pos[3]; float pad1;
610
float uv[2]; float pad2[2];
611
float color[4];
612
};
613
614
int size = size_u * size_v;
615
616
int ssboAlignment = vulkan_->GetPhysicalDeviceProperties().properties.limits.minStorageBufferOffsetAlignment;
617
uint8_t *data = (uint8_t *)push_->Allocate(size * sizeof(TessData), ssboAlignment, &bufInfo_[0].buffer, (uint32_t *)&bufInfo_[0].offset);
618
bufInfo_[0].range = size * sizeof(TessData);
619
620
float *pos = (float *)(data);
621
float *tex = (float *)(data + offsetof(TessData, uv));
622
float *col = (float *)(data + offsetof(TessData, color));
623
int stride = sizeof(TessData) / sizeof(float);
624
625
CopyControlPoints(pos, tex, col, stride, stride, stride, points, size, vertType);
626
627
using Spline::Weight;
628
629
// Weights U
630
data = (uint8_t *)push_->Allocate(weights.size_u * sizeof(Weight), ssboAlignment, &bufInfo_[1].buffer, (uint32_t *)&bufInfo_[1].offset);
631
memcpy(data, weights.u, weights.size_u * sizeof(Weight));
632
bufInfo_[1].range = weights.size_u * sizeof(Weight);
633
634
// Weights V
635
data = (uint8_t *)push_->Allocate(weights.size_v * sizeof(Weight), ssboAlignment, &bufInfo_[2].buffer, (uint32_t *)&bufInfo_[2].offset);
636
memcpy(data, weights.v, weights.size_v * sizeof(Weight));
637
bufInfo_[2].range = weights.size_v * sizeof(Weight);
638
}
639
640