diff --git a/GPU/D3D11/DrawEngineD3D11.cpp b/GPU/D3D11/DrawEngineD3D11.cpp index e801f49d6d..6bc808ef6a 100644 --- a/GPU/D3D11/DrawEngineD3D11.cpp +++ b/GPU/D3D11/DrawEngineD3D11.cpp @@ -20,6 +20,8 @@ #include "Common/Log.h" #include "Common/MemoryUtil.h" #include "Common/TimeUtil.h" +#include "Common/Profiler/Profiler.h" + #include "Core/MemMap.h" #include "Core/System.h" #include "Core/Reporting.h" @@ -273,6 +275,8 @@ void DrawEngineD3D11::BeginFrame() { pushVerts_->Reset(); pushInds_->Reset(); + gpuStats.numTrackedVertexArrays = (int)vai_.size(); + if (--decimationCounter_ <= 0) { decimationCounter_ = VERTEXCACHE_DECIMATION_INTERVAL; } else { @@ -321,24 +325,31 @@ VertexArrayInfoD3D11::~VertexArrayInfoD3D11() { // The inline wrapper in the header checks for numDrawCalls == 0 void DrawEngineD3D11::DoFlush() { gpuStats.numFlushes++; - gpuStats.numTrackedVertexArrays = (int)vai_.size(); // In D3D, we're synchronous and state carries over so all we reset here on a new step is the viewport/scissor. int curRenderStepId = draw_->GetCurrentStepId(); if (lastRenderStepId_ != curRenderStepId) { // Dirty everything that has dynamic state that will need re-recording. gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS); + textureCache_->ForgetLastTexture(); lastRenderStepId_ = curRenderStepId; } + bool textureNeedsApply = false; + if (gstate_c.IsDirty(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS) && !gstate.isModeClear() && gstate.isTextureMapEnabled()) { + textureCache_->SetTexture(); + gstate_c.Clean(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS); + textureNeedsApply = true; + } else if (gstate.getTextureAddress(0) == ((gstate.getFrameBufRawAddress() | 0x04000000) & 0x3FFFFFFF)) { + // This catches the case of clearing a texture. (#10957) + gstate_c.Dirty(DIRTY_TEXTURE_IMAGE); + } + // This is not done on every drawcall, we collect vertex data // until critical state changes. That's when we draw (flush). GEPrimitiveType prim = prevPrim_; - // SetTexture is called in here, along with setting a lot of other state. - ApplyDrawState(prim); - // Always use software for flat shading to fix the provoking index. bool tess = gstate_c.submitType == SubmitType::HW_BEZIER || gstate_c.submitType == SubmitType::HW_SPLINE; bool useHWTransform = CanUseHardwareTransform(prim) && (tess || gstate.getShadeMode() != GE_SHADE_FLAT); @@ -510,7 +521,6 @@ rotateVBO: prim = indexGen.Prim(); } - VERBOSE_LOG(G3D, "Flush prim %d! %d verts in one go", prim, vertexCount); bool hasColor = (lastVType_ & GE_VTYPE_COL_MASK) != GE_VTYPE_COL_NONE; if (gstate.isModeThrough()) { gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && (hasColor || gstate.getMaterialAmbientA() == 255); @@ -518,6 +528,12 @@ rotateVBO: gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && ((hasColor && (gstate.materialupdate & 1)) || gstate.getMaterialAmbientA() == 255) && (!gstate.isLightingEnabled() || gstate.getAmbientA() == 255); } + if (textureNeedsApply) { + textureCache_->ApplyTexture(); + } + + // Need to ApplyDrawState after ApplyTexture because depal can launch a render pass and that wrecks the state. + ApplyDrawState(prim); ApplyDrawStateLate(true, dynState_.stencilRef); D3D11VertexShader *vshader; @@ -532,6 +548,7 @@ rotateVBO: context_->IASetInputLayout(inputLayout); UINT stride = dec_->GetDecVtxFmt().stride; context_->IASetPrimitiveTopology(d3d11prim[prim]); + if (!vb_) { // Push! UINT vOffset; @@ -563,6 +580,7 @@ rotateVBO: } } } else { + PROFILE_THIS_SCOPE("soft"); DecodeVerts(decoded); bool hasColor = (lastVType_ & GE_VTYPE_COL_MASK) != GE_VTYPE_COL_NONE; if (gstate.isModeThrough()) { @@ -611,15 +629,22 @@ rotateVBO: swTransform.Decode(prim, dec_->VertexType(), dec_->GetDecVtxFmt(), maxIndex, &result); if (result.action == SW_NOT_READY) { swTransform.DetectOffsetTexture(maxIndex); - swTransform.BuildDrawingParams(prim, indexGen.VertexCount(), dec_->VertexType(), inds, maxIndex, &result); } + if (textureNeedsApply) + textureCache_->ApplyTexture(); + + // Need to ApplyDrawState after ApplyTexture because depal can launch a render pass and that wrecks the state. + ApplyDrawState(prim); + + if (result.action == SW_NOT_READY) + swTransform.BuildDrawingParams(prim, indexGen.VertexCount(), dec_->VertexType(), inds, maxIndex, &result); if (result.setSafeSize) framebufferManager_->SetSafeSize(result.safeWidth, result.safeHeight); - if (result.action == SW_DRAW_PRIMITIVES) { - ApplyDrawStateLate(result.setStencil, result.stencilValue); + ApplyDrawStateLate(result.setStencil, result.stencilValue); + if (result.action == SW_DRAW_PRIMITIVES) { D3D11VertexShader *vshader; D3D11FragmentShader *fshader; shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, false, false, decOptions_.expandAllWeightsToFloat); diff --git a/GPU/D3D11/StateMappingD3D11.cpp b/GPU/D3D11/StateMappingD3D11.cpp index 9d5fa2a2d4..843da3d4e7 100644 --- a/GPU/D3D11/StateMappingD3D11.cpp +++ b/GPU/D3D11/StateMappingD3D11.cpp @@ -124,8 +124,6 @@ static const D3D11_LOGIC_OP logicOps[] = { void DrawEngineD3D11::ResetFramebufferRead() { if (fboTexBound_) { - ID3D11ShaderResourceView *srv = nullptr; - context_->PSSetShaderResources(0, 1, &srv); fboTexBound_ = false; } } @@ -167,6 +165,18 @@ void DrawEngineD3D11::ApplyDrawState(int prim) { ApplyFramebufferRead(&fboTexNeedsBind_); // The shader takes over the responsibility for blending, so recompute. ApplyStencilReplaceAndLogicOpIgnoreBlend(blendState.replaceAlphaWithStencil, blendState); + + if (fboTexNeedsBind_) { + framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY); + // No sampler required, we do a plain Load in the pixel shader. + fboTexBound_ = true; + fboTexNeedsBind_ = false; + + framebufferManager_->RebindFramebuffer("RebindFramebuffer - ApplyDrawState"); + // Must dirty blend state here so we re-copy next time. Example: Lunar's spell effects. + gstate_c.Dirty(DIRTY_BLEND_STATE); + } + gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); } else if (blendState.resetFramebufferRead) { ResetFramebufferRead(); @@ -416,16 +426,6 @@ void DrawEngineD3D11::ApplyDrawState(int prim) { } void DrawEngineD3D11::ApplyDrawStateLate(bool applyStencilRef, uint8_t stencilRef) { - if (!gstate.isModeClear()) { - if (fboTexNeedsBind_) { - framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY); - // No sampler required, we do a plain Load in the pixel shader. - fboTexBound_ = true; - fboTexNeedsBind_ = false; - } - textureCache_->ApplyTexture(); - } - // we go through Draw here because it automatically handles screen rotation, as needed in UWP on mobiles. if (gstate_c.IsDirty(DIRTY_VIEWPORTSCISSOR_STATE)) { draw_->SetViewports(1, &dynState_.viewport); @@ -435,7 +435,7 @@ void DrawEngineD3D11::ApplyDrawStateLate(bool applyStencilRef, uint8_t stencilRe context_->RSSetState(rasterState_); } if (gstate_c.IsDirty(DIRTY_BLEND_STATE)) { - // Need to do this AFTER ApplyTexture because the process of depallettization can ruin the blend state. + // Need to do this AFTER ApplyTexture because the process of depalettization can ruin the blend state. float blendColor[4]; Uint8x4ToFloat4(blendColor, dynState_.blendColor); if (device1_) { diff --git a/GPU/D3D11/TextureCacheD3D11.cpp b/GPU/D3D11/TextureCacheD3D11.cpp index 9fef0f602e..15dce6816c 100644 --- a/GPU/D3D11/TextureCacheD3D11.cpp +++ b/GPU/D3D11/TextureCacheD3D11.cpp @@ -435,16 +435,17 @@ void TextureCacheD3D11::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer, CheckAlphaResult alphaStatus = CheckAlpha(clutBuf_, GetClutDestFormatD3D11(clutFormat), clutTotalColors); gstate_c.SetTextureFullAlpha(alphaStatus == CHECKALPHA_FULL); } else { - gstate_c.SetTextureFullAlpha(gstate.getTextureFormat() == GE_TFMT_5650); framebufferManager_->RebindFramebuffer("RebindFramebuffer - ApplyTextureFramebuffer"); framebufferManager_->BindFramebufferAsColorTexture(0, framebuffer, BINDFBCOLOR_MAY_COPY_WITH_UV | BINDFBCOLOR_APPLY_TEX_OFFSET); + gstate_c.SetTextureFullAlpha(gstate.getTextureFormat() == GE_TFMT_5650); } SamplerCacheKey samplerKey = GetFramebufferSamplingParams(framebuffer->bufferWidth, framebuffer->bufferHeight); ID3D11SamplerState *state = samplerCache_.GetOrCreateSampler(device_, samplerKey); context_->PSSetSamplers(0, 1, &state); - gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_BLEND_STATE | DIRTY_FRAGMENTSHADER_STATE); + gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_BLEND_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS); + draw_->InvalidateCachedState(); } void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) { diff --git a/GPU/Directx9/DrawEngineDX9.cpp b/GPU/Directx9/DrawEngineDX9.cpp index 4130d54cff..d69c03da21 100644 --- a/GPU/Directx9/DrawEngineDX9.cpp +++ b/GPU/Directx9/DrawEngineDX9.cpp @@ -305,6 +305,8 @@ static uint32_t SwapRB(uint32_t c) { } void DrawEngineDX9::BeginFrame() { + gpuStats.numTrackedVertexArrays = (int)vai_.size(); + DecimateTrackedVertexArrays(); lastRenderStepId_ = -1; @@ -313,7 +315,6 @@ void DrawEngineDX9::BeginFrame() { // The inline wrapper in the header checks for numDrawCalls == 0 void DrawEngineDX9::DoFlush() { gpuStats.numFlushes++; - gpuStats.numTrackedVertexArrays = (int)vai_.size(); // In D3D, we're synchronous and state carries over so all we reset here on a new step is the viewport/scissor. int curRenderStepId = draw_->GetCurrentStepId(); @@ -323,14 +324,23 @@ void DrawEngineDX9::DoFlush() { lastRenderStepId_ = curRenderStepId; } - // This is not done on every drawcall, we should collect vertex data - // until critical state changes. That's when we draw (flush). + bool textureNeedsApply = false; + if (gstate_c.IsDirty(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS) && !gstate.isModeClear() && gstate.isTextureMapEnabled()) { + textureCache_->SetTexture(); + gstate_c.Clean(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS); + textureNeedsApply = true; + } else if (gstate.getTextureAddress(0) == ((gstate.getFrameBufRawAddress() | 0x04000000) & 0x3FFFFFFF)) { + // This catches the case of clearing a texture. (#10957) + gstate_c.Dirty(DIRTY_TEXTURE_IMAGE); + } + GEPrimitiveType prim = prevPrim_; - ApplyDrawState(prim); - VSShader *vshader = shaderManager_->ApplyShader(CanUseHardwareTransform(prim), useHWTessellation_, lastVType_, decOptions_.expandAllWeightsToFloat); + // Always use software for flat shading to fix the provoking index. + bool tess = gstate_c.submitType == SubmitType::HW_BEZIER || gstate_c.submitType == SubmitType::HW_SPLINE; + bool useHWTransform = CanUseHardwareTransform(prim) && (tess || gstate.getShadeMode() != GE_SHADE_FLAT); - if (vshader->UseHWTransform()) { + if (useHWTransform) { LPDIRECT3DVERTEXBUFFER9 vb_ = NULL; LPDIRECT3DINDEXBUFFER9 ib_ = NULL; @@ -500,7 +510,6 @@ rotateVBO: prim = indexGen.Prim(); } - VERBOSE_LOG(G3D, "Flush prim %i! %i verts in one go", prim, vertexCount); bool hasColor = (lastVType_ & GE_VTYPE_COL_MASK) != GE_VTYPE_COL_NONE; if (gstate.isModeThrough()) { gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && (hasColor || gstate.getMaterialAmbientA() == 255); @@ -508,8 +517,14 @@ rotateVBO: gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && ((hasColor && (gstate.materialupdate & 1)) || gstate.getMaterialAmbientA() == 255) && (!gstate.isLightingEnabled() || gstate.getAmbientA() == 255); } + if (textureNeedsApply) { + textureCache_->ApplyTexture(); + } + + ApplyDrawState(prim); ApplyDrawStateLate(); - vshader = shaderManager_->ApplyShader(CanUseHardwareTransform(prim), useHWTessellation_, lastVType_, decOptions_.expandAllWeightsToFloat); + + DX9::VSShader *vshader = shaderManager_->ApplyShader(true, useHWTessellation_, lastVType_, decOptions_.expandAllWeightsToFloat); IDirect3DVertexDeclaration9 *pHardwareVertexDecl = SetupDecFmtForDraw(vshader, dec_->GetDecVtxFmt(), dec_->VertexType()); if (pHardwareVertexDecl) { @@ -587,14 +602,21 @@ rotateVBO: swTransform.Decode(prim, dec_->VertexType(), dec_->GetDecVtxFmt(), maxIndex, &result); if (result.action == SW_NOT_READY) { swTransform.DetectOffsetTexture(maxIndex); - swTransform.BuildDrawingParams(prim, indexGen.VertexCount(), dec_->VertexType(), inds, maxIndex, &result); } + if (textureNeedsApply) + textureCache_->ApplyTexture(); + + ApplyDrawState(prim); + + if (result.action == SW_NOT_READY) + swTransform.BuildDrawingParams(prim, indexGen.VertexCount(), dec_->VertexType(), inds, maxIndex, &result); if (result.setSafeSize) framebufferManager_->SetSafeSize(result.safeWidth, result.safeHeight); ApplyDrawStateLate(); - vshader = shaderManager_->ApplyShader(false, false, lastVType_, decOptions_.expandAllWeightsToFloat); + + DX9::VSShader *vshader = shaderManager_->ApplyShader(false, false, lastVType_, decOptions_.expandAllWeightsToFloat); if (result.action == SW_DRAW_PRIMITIVES) { if (result.setStencil) { diff --git a/GPU/Directx9/ShaderManagerDX9.cpp b/GPU/Directx9/ShaderManagerDX9.cpp index 38cda2d8b1..99be9bd2ee 100644 --- a/GPU/Directx9/ShaderManagerDX9.cpp +++ b/GPU/Directx9/ShaderManagerDX9.cpp @@ -547,10 +547,6 @@ void ShaderManagerDX9::DirtyLastShader() { // disables vertex arrays } VSShader *ShaderManagerDX9::ApplyShader(bool useHWTransform, bool useHWTessellation, u32 vertType, bool weightsAsFloat) { - // Always use software for flat shading to fix the provoking index. - bool tess = gstate_c.submitType == SubmitType::HW_BEZIER || gstate_c.submitType == SubmitType::HW_SPLINE; - useHWTransform = useHWTransform && (tess || gstate.getShadeMode() != GE_SHADE_FLAT); - VShaderID VSID; if (gstate_c.IsDirty(DIRTY_VERTEXSHADER_STATE)) { gstate_c.Clean(DIRTY_VERTEXSHADER_STATE); diff --git a/GPU/Directx9/StateMappingDX9.cpp b/GPU/Directx9/StateMappingDX9.cpp index dadf797873..88d7f24342 100644 --- a/GPU/Directx9/StateMappingDX9.cpp +++ b/GPU/Directx9/StateMappingDX9.cpp @@ -91,14 +91,14 @@ static const D3DSTENCILOP stencilOps[] = { }; inline void DrawEngineDX9::ResetFramebufferRead() { - if (fboTexBound_) { - device_->SetTexture(1, nullptr); - fboTexBound_ = false; - } + fboTexBound_ = false; } void DrawEngineDX9::ApplyDrawState(int prim) { - // TODO: All this setup is soon so expensive that we'll need dirty flags, or simply do it in the command writes where we detect dirty by xoring. Silly to do all this work on every drawcall. + if (!gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE)) { + // nothing to do + return; + } if (gstate_c.IsDirty(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS) && !gstate.isModeClear() && gstate.isTextureMapEnabled()) { textureCache_->SetTexture(); @@ -114,7 +114,6 @@ void DrawEngineDX9::ApplyDrawState(int prim) { bool useBufferedRendering = framebufferManager_->UseBufferedRendering(); if (gstate_c.IsDirty(DIRTY_BLEND_STATE)) { - gstate_c.Clean(DIRTY_BLEND_STATE); // Unfortunately, this isn't implemented on DX9 yet. gstate_c.SetAllowFramebufferRead(false); if (gstate.isModeClear()) { @@ -140,6 +139,18 @@ void DrawEngineDX9::ApplyDrawState(int prim) { ApplyFramebufferRead(&fboTexNeedsBind_); // The shader takes over the responsibility for blending, so recompute. ApplyStencilReplaceAndLogicOpIgnoreBlend(blendState.replaceAlphaWithStencil, blendState); + + if (fboTexNeedsBind_) { + // Note that this is positions, not UVs, that we need the copy from. + framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY); + // If we are rendering at a higher resolution, linear is probably best for the dest color. + device_->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); + device_->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + fboTexBound_ = true; + fboTexNeedsBind_ = false; + gstate_c.Dirty(DIRTY_BLEND_STATE); + } + gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); } else if (blendState.resetFramebufferRead) { ResetFramebufferRead(); @@ -172,7 +183,6 @@ void DrawEngineDX9::ApplyDrawState(int prim) { } if (gstate_c.IsDirty(DIRTY_RASTER_STATE)) { - gstate_c.Clean(DIRTY_RASTER_STATE); bool wantCull = !gstate.isModeClear() && prim != GE_PRIM_RECTANGLES && prim > GE_PRIM_LINE_STRIP && gstate.isCullEnabled(); if (wantCull) { if (gstate.getCullMode() == 1) { @@ -192,7 +202,6 @@ void DrawEngineDX9::ApplyDrawState(int prim) { } if (gstate_c.IsDirty(DIRTY_DEPTHSTENCIL_STATE)) { - gstate_c.Clean(DIRTY_DEPTHSTENCIL_STATE); GenericStencilFuncState stencilState; ConvertStencilFuncState(stencilState); @@ -254,7 +263,6 @@ void DrawEngineDX9::ApplyDrawState(int prim) { } if (gstate_c.IsDirty(DIRTY_VIEWPORTSCISSOR_STATE)) { - gstate_c.Clean(DIRTY_VIEWPORTSCISSOR_STATE); ViewportAndScissor vpAndScissor; ConvertViewportAndScissor(useBufferedRendering, framebufferManager_->GetRenderWidth(), framebufferManager_->GetRenderHeight(), @@ -275,26 +283,14 @@ void DrawEngineDX9::ApplyDrawState(int prim) { gstate_c.Dirty(DIRTY_DEPTHRANGE); } } + + gstate_c.Clean(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_BLEND_STATE); } void DrawEngineDX9::ApplyDrawStateLate() { // At this point, we know if the vertices are full alpha or not. // TODO: Set the nearest/linear here (since we correctly know if alpha/color tests are needed)? - if (!gstate.isModeClear()) { - textureCache_->ApplyTexture(); - if (fboTexNeedsBind_) { - // Note that this is positions, not UVs, that we need the copy from. - framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY); - // If we are rendering at a higher resolution, linear is probably best for the dest color. - device_->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); - device_->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); - fboTexBound_ = true; - fboTexNeedsBind_ = false; - } - - // TODO: Test texture? - } } } diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index 31ade7552d..ab0960abd5 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -148,7 +148,7 @@ void DrawEngineGLES::ClearInputLayoutMap() { } void DrawEngineGLES::BeginFrame() { - DecimateTrackedVertexArrays(); + gpuStats.numTrackedVertexArrays = 0; FrameData &frameData = frameData_[render_->GetCurFrame()]; render_->BeginPushBuffer(frameData.pushIndex); @@ -238,11 +238,9 @@ void *DrawEngineGLES::DecodeVertsToPushBuffer(GLPushBuffer *push, uint32_t *bind void DrawEngineGLES::DoFlush() { PROFILE_THIS_SCOPE("flush"); - FrameData &frameData = frameData_[render_->GetCurFrame()]; gpuStats.numFlushes++; - gpuStats.numTrackedVertexArrays = 0; // A new render step means we need to flush any dynamic state. Really, any state that is reset in // GLQueueRunner::PerformRenderPass. @@ -277,7 +275,6 @@ void DrawEngineGLES::DoFlush() { if (vshader->UseHWTransform()) { int vertexCount = 0; bool useElements = true; - bool populateCache = false; if (g_Config.bSoftwareSkinning && (lastVType_ & GE_VTYPE_WEIGHT_MASK)) { // If software skinning, we've already predecoded into "decoded". So push that content. @@ -301,7 +298,6 @@ void DrawEngineGLES::DoFlush() { } prim = indexGen.Prim(); - VERBOSE_LOG(G3D, "Flush prim %d! %d verts in one go", prim, vertexCount); bool hasColor = (lastVType_ & GE_VTYPE_COL_MASK) != GE_VTYPE_COL_NONE; if (gstate.isModeThrough()) { gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && (hasColor || gstate.getMaterialAmbientA() == 255); @@ -309,8 +305,9 @@ void DrawEngineGLES::DoFlush() { gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && ((hasColor && (gstate.materialupdate & 1)) || gstate.getMaterialAmbientA() == 255) && (!gstate.isLightingEnabled() || gstate.getAmbientA() == 255); } - if (textureNeedsApply) + if (textureNeedsApply) { textureCache_->ApplyTexture(); + } // Need to ApplyDrawState after ApplyTexture because depal can launch a render pass and that wrecks the state. ApplyDrawState(prim); @@ -331,6 +328,7 @@ void DrawEngineGLES::DoFlush() { render_->Draw(glprim[prim], 0, vertexCount); } } else { + PROFILE_THIS_SCOPE("soft"); DecodeVerts(decoded); bool hasColor = (lastVType_ & GE_VTYPE_COL_MASK) != GE_VTYPE_COL_NONE; if (gstate.isModeThrough()) { @@ -354,7 +352,7 @@ void DrawEngineGLES::DoFlush() { params.transformedExpanded = transformedExpanded; params.fbman = framebufferManager_; params.texCache = textureCache_; - params.allowClear = true; + params.allowClear = true; // Clear in OpenGL respects scissor rects, so we'll use it. params.allowSeparateAlphaClear = true; params.provokeFlatFirst = false; params.flippedY = framebufferManager_->UseBufferedRendering(); diff --git a/GPU/GLES/DrawEngineGLES.h b/GPU/GLES/DrawEngineGLES.h index c016771e64..8d45ff0d84 100644 --- a/GPU/GLES/DrawEngineGLES.h +++ b/GPU/GLES/DrawEngineGLES.h @@ -115,7 +115,6 @@ public: protected: bool UpdateUseHWTessellation(bool enable) override; - void DecimateTrackedVertexArrays() {} private: void InitDeviceObjects(); diff --git a/GPU/GLES/StateMappingGLES.cpp b/GPU/GLES/StateMappingGLES.cpp index 08e61ee56b..55af56b039 100644 --- a/GPU/GLES/StateMappingGLES.cpp +++ b/GPU/GLES/StateMappingGLES.cpp @@ -183,6 +183,7 @@ void DrawEngineGLES::ApplyDrawState(int prim) { gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); } else if (blendState.resetFramebufferRead) { ResetFramebufferRead(); + gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); } if (blendState.enabled) { diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 0c464853e6..e50e6477ba 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -268,6 +268,8 @@ void DrawEngineVulkan::DeviceRestore(Draw::DrawContext *draw) { } void DrawEngineVulkan::BeginFrame() { + gpuStats.numTrackedVertexArrays = (int)vai_.size(); + lastPipeline_ = nullptr; lastRenderStepId_ = -1; @@ -522,16 +524,17 @@ void MarkUnreliable(VertexArrayInfoVulkan *vai) { // The inline wrapper in the header checks for numDrawCalls == 0 void DrawEngineVulkan::DoFlush() { - PROFILE_THIS_SCOPE("Flush"); - gpuStats.numFlushes++; - // TODO: Should be enough to update this once per frame? - gpuStats.numTrackedVertexArrays = (int)vai_.size(); - VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); + + PROFILE_THIS_SCOPE("Flush"); + FrameData &frameData = GetCurFrame(); + + gpuStats.numFlushes++; // TODO: Needs to be behind a check for changed render pass, at an appropriate time in this function. // Similar issues as with the lastRenderStepId_ check. Will need a bit of a rethink. lastPipeline_ = nullptr; + // If have a new render pass, dirty our dynamic state so it gets re-set. // We have to do this again after the last possible place in DoFlush that can cause a renderpass switch // like a shader blend blit or similar. But before we actually set the state! @@ -543,8 +546,6 @@ void DrawEngineVulkan::DoFlush() { lastRenderStepId_ = curRenderStepId; } - FrameData *frame = &GetCurFrame(); - bool tess = gstate_c.submitType == SubmitType::HW_BEZIER || gstate_c.submitType == SubmitType::HW_SPLINE; bool textureNeedsApply = false; @@ -569,10 +570,7 @@ void DrawEngineVulkan::DoFlush() { uint32_t vbOffset; if (useHWTransform) { - // We don't detect clears in this path, so here we can switch framebuffers if necessary. - int vertexCount = 0; - int maxIndex; bool useElements = true; // Cannot cache vertex data with morph enabled. @@ -602,7 +600,7 @@ void DrawEngineVulkan::DoFlush() { vai->minihash = ComputeMiniHash(); vai->status = VertexArrayInfoVulkan::VAI_HASHING; vai->drawsUntilNextFullHash = 0; - DecodeVertsToPushBuffer(frame->pushVertex, &vbOffset, &vbuf); // writes to indexGen + DecodeVertsToPushBuffer(frameData.pushVertex, &vbOffset, &vbuf); // writes to indexGen vai->numVerts = indexGen.VertexCount(); vai->prim = indexGen.Prim(); vai->maxIndex = indexGen.MaxIndex(); @@ -628,7 +626,7 @@ void DrawEngineVulkan::DoFlush() { } if (newMiniHash != vai->minihash || newHash != vai->hash) { MarkUnreliable(vai); - DecodeVertsToPushBuffer(frame->pushVertex, &vbOffset, &vbuf); + DecodeVertsToPushBuffer(frameData.pushVertex, &vbOffset, &vbuf); goto rotateVBO; } if (vai->numVerts > 64) { @@ -647,7 +645,7 @@ void DrawEngineVulkan::DoFlush() { u32 newMiniHash = ComputeMiniHash(); if (newMiniHash != vai->minihash) { MarkUnreliable(vai); - DecodeVertsToPushBuffer(frame->pushVertex, &vbOffset, &vbuf); + DecodeVertsToPushBuffer(frameData.pushVertex, &vbOffset, &vbuf); goto rotateVBO; } } @@ -683,7 +681,6 @@ void DrawEngineVulkan::DoFlush() { vbOffset = vai->vbOffset; ibOffset = vai->ibOffset; vertexCount = vai->numVerts; - maxIndex = vai->maxIndex; prim = static_cast(vai->prim); break; } @@ -702,7 +699,6 @@ void DrawEngineVulkan::DoFlush() { vbOffset = vai->vbOffset; ibOffset = vai->ibOffset; vertexCount = vai->numVerts; - maxIndex = vai->maxIndex; prim = static_cast(vai->prim); gstate_c.vertexFullAlpha = vai->flags & VAIVULKAN_FLAG_VERTEXFULLALPHA; @@ -715,7 +711,7 @@ void DrawEngineVulkan::DoFlush() { if (vai->lastFrame != gpuStats.numFlips) { vai->numFrames++; } - DecodeVertsToPushBuffer(frame->pushVertex, &vbOffset, &vbuf); + DecodeVertsToPushBuffer(frameData.pushVertex, &vbOffset, &vbuf); goto rotateVBO; } default: @@ -725,11 +721,11 @@ void DrawEngineVulkan::DoFlush() { if (g_Config.bSoftwareSkinning && (lastVType_ & GE_VTYPE_WEIGHT_MASK)) { // If software skinning, we've already predecoded into "decoded". So push that content. VkDeviceSize size = decodedVerts_ * dec_->GetDecVtxFmt().stride; - u8 *dest = (u8 *)frame->pushVertex->Push(size, &vbOffset, &vbuf); + u8 *dest = (u8 *)frameData.pushVertex->Push(size, &vbOffset, &vbuf); memcpy(dest, decoded, size); } else { // Decode directly into the pushbuffer - DecodeVertsToPushBuffer(frame->pushVertex, &vbOffset, &vbuf); + DecodeVertsToPushBuffer(frameData.pushVertex, &vbOffset, &vbuf); } rotateVBO: @@ -749,8 +745,6 @@ void DrawEngineVulkan::DoFlush() { gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && ((hasColor && (gstate.materialupdate & 1)) || gstate.getMaterialAmbientA() == 255) && (!gstate.isLightingEnabled() || gstate.getAmbientA() == 255); } - PROFILE_THIS_SCOPE("updatestate"); - if (textureNeedsApply) { textureCache_->ApplyTexture(); textureCache_->GetVulkanHandles(imageView, sampler); @@ -809,17 +803,16 @@ void DrawEngineVulkan::DoFlush() { lastPrim_ = prim; dirtyUniforms_ |= shaderManager_->UpdateUniforms(framebufferManager_->UseBufferedRendering()); - UpdateUBOs(frame); + UpdateUBOs(&frameData); VkDescriptorSet ds = GetOrCreateDescriptorSet(imageView, sampler, baseBuf, lightBuf, boneBuf, tess); const uint32_t dynamicUBOOffsets[3] = { baseUBOOffset, lightUBOOffset, boneUBOOffset, }; - if (useElements) { if (!ibuf) { - ibOffset = (uint32_t)frame->pushIndex->Push(decIndex, sizeof(uint16_t) * indexGen.VertexCount(), &ibuf); + ibOffset = (uint32_t)frameData.pushIndex->Push(decIndex, sizeof(uint16_t) * indexGen.VertexCount(), &ibuf); } renderManager->DrawIndexed(pipelineLayout_, ds, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, vertexCount, 1, VK_INDEX_TYPE_UINT16); } else { @@ -827,8 +820,7 @@ void DrawEngineVulkan::DoFlush() { } } else { PROFILE_THIS_SCOPE("soft"); - // Decode to "decoded" - DecodeVertsToPushBuffer(nullptr, nullptr, nullptr); + DecodeVerts(decoded); bool hasColor = (lastVType_ & GE_VTYPE_COL_MASK) != GE_VTYPE_COL_NONE; if (gstate.isModeThrough()) { gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && (hasColor || gstate.getMaterialAmbientA() == 255); @@ -841,7 +833,6 @@ void DrawEngineVulkan::DoFlush() { // Undo the strip optimization, not supported by the SW code yet. if (prim == GE_PRIM_TRIANGLE_STRIP) prim = GE_PRIM_TRIANGLES; - VERBOSE_LOG(G3D, "Flush prim %i SW! %i verts in one go", prim, indexGen.VertexCount()); u16 *inds = decIndex; SoftwareTransformResult result{}; @@ -851,7 +842,7 @@ void DrawEngineVulkan::DoFlush() { params.transformedExpanded = transformedExpanded; params.fbman = framebufferManager_; params.texCache = textureCache_; - // We have to force drawing of primitives if !framebufferManager_->UseBufferedRendering() because Vulkan clears + // In Vulkan, we have to force drawing of primitives if !framebufferManager_->UseBufferedRendering() because Vulkan clears // do not respect scissor rects. params.allowClear = framebufferManager_->UseBufferedRendering(); params.allowSeparateAlphaClear = false; @@ -940,12 +931,13 @@ void DrawEngineVulkan::DoFlush() { if (fboTexBound_) gstate_c.Dirty(DIRTY_BLEND_STATE); } + lastPrim_ = prim; dirtyUniforms_ |= shaderManager_->UpdateUniforms(framebufferManager_->UseBufferedRendering()); // Even if the first draw is through-mode, make sure we at least have one copy of these uniforms buffered - UpdateUBOs(frame); + UpdateUBOs(&frameData); VkDescriptorSet ds = GetOrCreateDescriptorSet(imageView, sampler, baseBuf, lightBuf, boneBuf, tess); const uint32_t dynamicUBOOffsets[3] = { @@ -956,12 +948,12 @@ void DrawEngineVulkan::DoFlush() { if (result.drawIndexed) { VkBuffer vbuf, ibuf; - vbOffset = (uint32_t)frame->pushVertex->Push(result.drawBuffer, maxIndex * sizeof(TransformedVertex), &vbuf); - ibOffset = (uint32_t)frame->pushIndex->Push(inds, sizeof(short) * result.drawNumTrans, &ibuf); + vbOffset = (uint32_t)frameData.pushVertex->Push(result.drawBuffer, maxIndex * sizeof(TransformedVertex), &vbuf); + ibOffset = (uint32_t)frameData.pushIndex->Push(inds, sizeof(short) * result.drawNumTrans, &ibuf); renderManager->DrawIndexed(pipelineLayout_, ds, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, result.drawNumTrans, 1, VK_INDEX_TYPE_UINT16); } else { VkBuffer vbuf; - vbOffset = (uint32_t)frame->pushVertex->Push(result.drawBuffer, result.drawNumTrans * sizeof(TransformedVertex), &vbuf); + vbOffset = (uint32_t)frameData.pushVertex->Push(result.drawBuffer, result.drawNumTrans * sizeof(TransformedVertex), &vbuf); renderManager->Draw(pipelineLayout_, ds, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, result.drawNumTrans); } } else if (result.action == SW_CLEAR) {