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