diff --git a/pcsx2/GS/GSState.cpp b/pcsx2/GS/GSState.cpp index 8175653a2d..9b86f612fc 100644 --- a/pcsx2/GS/GSState.cpp +++ b/pcsx2/GS/GSState.cpp @@ -2616,6 +2616,7 @@ void GSState::FlushPrim() m_quad_check_valid_shuffle = false; m_drawlist.clear(); m_drawlist_bbox.clear(); + m_drawlist_bbox_tex.clear(); if (GSConfig.ShouldDump(s_n, g_perfmon.GetFrame())) { @@ -4725,6 +4726,71 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlist(bool save_drawlist, b } } +template +void GSState::GetPrimitiveOverlapDrawlistTextureBBoxImpl(float bbox_scale) +{ + pxAssert(m_drawlist_bbox_tex.empty()); // Should only call this once per draw. + + constexpr int n = GSUtil::GetClassVertexCount(primclass); + const GSVertex* RESTRICT v = m_vertex->buff; + const u16* RESTRICT index = m_index->buff; + const u32 count = m_index->tail; + + for (u32 i = 0; i < count; i += n) + { + const float q = primclass == GS_SPRITE_CLASS ? v[index[i + 1]].RGBAQ.Q : v[index[i]].RGBAQ.Q; + GSVector4 bbox = GetTexCoordsImpl(v[index[i]], q); + + for (u32 j = 1; j < n; j++) + { + const GSVector4 tex = GetTexCoordsImpl(v[index[i + j]]); + bbox = bbox.min(tex).xyzw(bbox.max(tex)); + } + + bbox = bbox * bbox_scale; // Account for upscaling. + bbox = bbox.floor().xyzw(bbox.ceil()); // Round. + bbox += GSVector4(-1.0f, -1.0f, 1.0f, 1.0f); // +1 on all sides for bilinear. + + m_drawlist_bbox_tex.push_back(GSVector4i(bbox)); + } +} + +void GSState::GetPrimitiveOverlapDrawlistTextureBBox(float bbox_scale) +{ + pxAssert(PRIM->TME); + + switch (m_vt.m_primclass) + { + case GS_POINT_CLASS: + if (PRIM->FST) + GetPrimitiveOverlapDrawlistTextureBBoxImpl(bbox_scale); + else + GetPrimitiveOverlapDrawlistTextureBBoxImpl(bbox_scale); + break; + case GS_LINE_CLASS: + if (PRIM->FST) + GetPrimitiveOverlapDrawlistTextureBBoxImpl(bbox_scale); + else + GetPrimitiveOverlapDrawlistTextureBBoxImpl(bbox_scale); + break; + case GS_TRIANGLE_CLASS: + if (PRIM->FST) + GetPrimitiveOverlapDrawlistTextureBBoxImpl(bbox_scale); + else + GetPrimitiveOverlapDrawlistTextureBBoxImpl(bbox_scale); + break; + case GS_SPRITE_CLASS: + if (PRIM->FST) + GetPrimitiveOverlapDrawlistTextureBBoxImpl(bbox_scale); + else + GetPrimitiveOverlapDrawlistTextureBBoxImpl(bbox_scale); + break; + default: + pxAssert(false); + return; + } +} + GSState::PRIM_OVERLAP GSState::PrimitiveOverlap(bool save_drawlist) { // Assume that 1 triangle, 1 sprite, 1 line, or 3 points can't overlap. diff --git a/pcsx2/GS/GSState.h b/pcsx2/GS/GSState.h index dad4a86726..15bd230a72 100644 --- a/pcsx2/GS/GSState.h +++ b/pcsx2/GS/GSState.h @@ -392,6 +392,7 @@ public: PRIM_OVERLAP m_prim_overlap = PRIM_OVERLAP_UNKNOW; std::vector m_drawlist; std::vector m_drawlist_bbox; + std::vector m_drawlist_bbox_tex; struct GSPCRTCRegs { @@ -535,6 +536,9 @@ public: PRIM_OVERLAP GetPrimitiveOverlapDrawlistImpl(bool save_drawlist = false, bool save_bbox = false, float bbox_scale = 1.0f); PRIM_OVERLAP GetPrimitiveOverlapDrawlist(bool save_drawlist = false, bool save_bbox = false, float bbox_scale = 1.0f); PRIM_OVERLAP PrimitiveOverlap(bool save_drawlist = false); + template + void GetPrimitiveOverlapDrawlistTextureBBoxImpl(float bbox_scale = 1.0f); + void GetPrimitiveOverlapDrawlistTextureBBox(float bbox_scale = 1.0f); bool SpriteDrawWithoutGaps(); void CalculatePrimitiveCoversWithoutGaps(); GIFRegTEX0 GetTex0Layer(u32 lod); diff --git a/pcsx2/GS/Renderers/DX11/GSDevice11.cpp b/pcsx2/GS/Renderers/DX11/GSDevice11.cpp index 12210ee8e2..3c74ab9162 100644 --- a/pcsx2/GS/Renderers/DX11/GSDevice11.cpp +++ b/pcsx2/GS/Renderers/DX11/GSDevice11.cpp @@ -3027,6 +3027,58 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config) } } +void GSDevice11::FeedbackCopyAndBind(const GSHWDrawConfig& config, + GSTexture* rt, GSTexture* rt_clone, GSTexture* ds, GSTexture* ds_clone, const GSVector4i& copyarea) +{ + if (rt_clone) + { + CopyRect(rt, rt_clone, copyarea, copyarea.left, copyarea.top); + PSSetShaderResource(2, rt_clone); + if (config.tex_hazard == GSHWDrawConfig::TEX_HAZARD_RT) + PSSetShaderResource(0, rt_clone); + } + if (ds_clone) + { + CopyRect(ds, ds_clone, copyarea, copyarea.left, copyarea.top); + PSSetShaderResource(4, ds_clone); + if (config.tex_hazard == GSHWDrawConfig::TEX_HAZARD_DEPTH) + PSSetShaderResource(0, ds_clone); + } +} + +// Choose the best copy area based on the hazards and whether we need RT and/or DS copies. +void GSDevice11::FeedbackCopyAndBind(const GSHWDrawConfig& config, + GSTexture* rt, GSTexture* rt_clone, GSTexture* ds, GSTexture* ds_clone, + const GSVector4i& copyarea, const GSVector4i& samplearea) +{ + const GSVector4i rtsize = (rt ? rt : ds)->GetRect(); + + // DX11 can't do partial depth copies so only do attempt individual copies for RT hazards. + if (config.tex_hazard == GSHWDrawConfig::TEX_HAZARD_RT) + { + const GSVector4i union_rect = config.drawarea.runion(config.samplearea); + const u32 size_union = union_rect.width() * union_rect.height(); + const u32 size_indiv = config.drawarea.width() * config.drawarea.height() + + config.samplearea.width() * config.samplearea.height(); + + // Do an individual copy if the union is larger than the sum of individual areas. + if (size_union > size_indiv) + { + FeedbackCopyAndBind(config, rt, rt_clone, ds, ds_clone, ProcessCopyArea(rtsize, config.drawarea)); + FeedbackCopyAndBind(config, rt, rt_clone, ds, ds_clone, ProcessCopyArea(rtsize, config.samplearea)); + } + else + { + FeedbackCopyAndBind(config, rt, rt_clone, ds, ds_clone, ProcessCopyArea(rtsize, union_rect)); + } + } + else + { + // No RT hazards so just need the draw area (or full area for DS). + FeedbackCopyAndBind(config, rt, rt_clone, ds, ds_clone, ProcessCopyArea(rtsize, config.drawarea)); + } +}; + void GSDevice11::SendHWDraw(const GSHWDrawConfig& config, GSTexture* draw_rt_clone, GSTexture* draw_rt, GSTexture* draw_ds_clone, GSTexture* draw_ds, const bool one_barrier, const bool full_barrier) @@ -3036,27 +3088,6 @@ void GSDevice11::SendHWDraw(const GSHWDrawConfig& config, Console.Warning("D3D11: Possible unnecessary copy detected."); #endif - auto CopyAndBind = [&](GSVector4i drawarea) { - if (draw_rt_clone) - { - CopyRect(draw_rt, draw_rt_clone, drawarea, drawarea.left, drawarea.top); - if ((one_barrier || full_barrier)) - PSSetShaderResource(2, draw_rt_clone); - if (config.tex_hazard == GSHWDrawConfig::TEX_HAZARD_RT) - PSSetShaderResource(0, draw_rt_clone); - } - if (draw_ds_clone) - { - CopyRect(draw_ds, draw_ds_clone, drawarea, drawarea.left, drawarea.top); - if ((one_barrier || full_barrier)) - PSSetShaderResource(4, draw_ds_clone); - if (config.tex_hazard == GSHWDrawConfig::TEX_HAZARD_DEPTH) - PSSetShaderResource(0, draw_ds_clone); - } - }; - - const GSVector4i rtsize(0, 0, (draw_rt ? draw_rt : draw_ds)->GetWidth(), (draw_rt ? draw_rt : draw_ds)->GetHeight()); - if (full_barrier) { const u32 draw_list_size = static_cast(config.drawlist->size()); @@ -3067,10 +3098,13 @@ void GSDevice11::SendHWDraw(const GSHWDrawConfig& config, for (u32 n = 0, p = 0; n < draw_list_size; n++) { - const u32 count = (*config.drawlist)[n] * indices_per_prim; + const u32 count = config.drawlist->at(n) * indices_per_prim; - const GSVector4i original_bbox = (*config.drawlist_bbox)[n].rintersect(config.drawarea); - CopyAndBind(ProcessCopyArea(rtsize, original_bbox)); + const GSVector4i bbox = config.drawlist_bbox->at(n).rintersect(config.drawarea); + const GSVector4i bbox_tex = config.drawlist_bbox_tex ? + config.drawlist_bbox_tex->at(n).rintersect(config.samplearea) : GSVector4i::zero(); + + FeedbackCopyAndBind(config, draw_rt, draw_rt_clone, draw_ds, draw_ds_clone, bbox, bbox_tex); Draw(config, p, count); @@ -3082,29 +3116,7 @@ void GSDevice11::SendHWDraw(const GSHWDrawConfig& config, if (one_barrier) { - // DX11 can't do partial depth copies. - if (config.tex_hazard == GSHWDrawConfig::TEX_HAZARD_RT) - { - const GSVector4i union_rect = config.drawarea.runion(config.samplearea); - const u32 size_union = union_rect.width() * union_rect.height(); - const u32 size_indiv = config.drawarea.width() * config.drawarea.height() + - config.samplearea.width() * config.samplearea.height(); - - // Do an individual copy if the union is larger than the sum of individual areas. - if (size_union > size_indiv) - { - CopyAndBind(ProcessCopyArea(rtsize, config.drawarea)); - CopyAndBind(ProcessCopyArea(rtsize, config.samplearea)); - } - else - { - CopyAndBind(ProcessCopyArea(rtsize, union_rect)); - } - } - else - { - CopyAndBind(ProcessCopyArea(rtsize, config.drawarea)); - } + FeedbackCopyAndBind(config, draw_rt, draw_rt_clone, draw_ds, draw_ds_clone, config.drawarea, config.samplearea); } Draw(config); diff --git a/pcsx2/GS/Renderers/DX11/GSDevice11.h b/pcsx2/GS/Renderers/DX11/GSDevice11.h index 5729c1ec3e..0c44060c46 100644 --- a/pcsx2/GS/Renderers/DX11/GSDevice11.h +++ b/pcsx2/GS/Renderers/DX11/GSDevice11.h @@ -372,6 +372,12 @@ public: void SetupOM(OMDepthStencilSelector dssel, OMBlendSelector bsel, u8 afix); void RenderHW(GSHWDrawConfig& config) override; + + void FeedbackCopyAndBind(const GSHWDrawConfig& config, + GSTexture* rt, GSTexture* rt_clone, GSTexture* ds, GSTexture* ds_clone, const GSVector4i& copyarea); + void FeedbackCopyAndBind(const GSHWDrawConfig& config, + GSTexture* rt, GSTexture* rt_clone, GSTexture* ds, GSTexture* ds_clone, + const GSVector4i& copyarea, const GSVector4i& samplearea); void SendHWDraw(const GSHWDrawConfig& config, GSTexture* draw_rt_clone, GSTexture* draw_rt, GSTexture* draw_ds_clone, GSTexture* draw_ds, const bool one_barrier, const bool full_barrier); diff --git a/pcsx2/GS/Renderers/DX12/GSDevice12.cpp b/pcsx2/GS/Renderers/DX12/GSDevice12.cpp index baf13f9910..64b60dcd32 100644 --- a/pcsx2/GS/Renderers/DX12/GSDevice12.cpp +++ b/pcsx2/GS/Renderers/DX12/GSDevice12.cpp @@ -4585,7 +4585,7 @@ void GSDevice12::SendHWDraw(const PipelineSelector& pipe, const GSHWDrawConfig& for (u32 n = 0, p = 0; n < draw_list_size; n++) { - const u32 count = (*config.drawlist)[n] * indices_per_prim; + const u32 count = config.drawlist->at(n) * indices_per_prim; if (feedback_rt) FeedbackBarrier(draw_rt); diff --git a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp index cb6ba5ddb6..30088ef96a 100644 --- a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp +++ b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp @@ -6161,7 +6161,7 @@ void GSRendererHW::DetermineVSConfig(GSTextureCache::Target* rt, float rtscale, m_conf.vs.iip = !IsFlatShaded(); } -void GSRendererHW::DetermineBarriers(GSTextureCache::Target* rt) +void GSRendererHW::DetermineBarriers(GSTextureCache::Target* rt, GSTextureCache::Source* tex) { const GSDevice::FeatureSupport& features = g_gs_device->Features(); @@ -6204,6 +6204,16 @@ void GSRendererHW::DetermineBarriers(GSTextureCache::Target* rt) ComputeDrawlistGetSize(rt->m_scale); m_conf.drawlist = &m_drawlist; m_conf.drawlist_bbox = &m_drawlist_bbox; + + if (m_conf.tex_hazard != GSHWDrawConfig::TEX_HAZARD_NONE) + { + GetPrimitiveOverlapDrawlistTextureBBox(tex->GetScale()); + m_conf.drawlist_bbox_tex = &m_drawlist_bbox_tex; + } + else + { + m_conf.drawlist_bbox_tex = nullptr; + } } } @@ -9027,7 +9037,7 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta } // Barriers must be determined before indices are modified via HandleProvokingVertexFirst/SetupIA. - DetermineBarriers(rt); + DetermineBarriers(rt, tex); // Perform second pass setup here once barriers are determined. EmulateAlphaTestSecondPass(); diff --git a/pcsx2/GS/Renderers/HW/GSRendererHW.h b/pcsx2/GS/Renderers/HW/GSRendererHW.h index 4684b7fec2..5c7fc86b27 100644 --- a/pcsx2/GS/Renderers/HW/GSRendererHW.h +++ b/pcsx2/GS/Renderers/HW/GSRendererHW.h @@ -251,7 +251,7 @@ private: void DetermineVSConfig(GSTextureCache::Target* rt, float rtscale, const GSVector2i& rtsize, const GSVector2i& unscaled_size, float& vs_scale_x, float& vs_scale_y); - void DetermineBarriers(GSTextureCache::Target* rt); + void DetermineBarriers(GSTextureCache::Target* rt, GSTextureCache::Source* tex); void SetTCOffset(); bool NextDrawColClip() const; diff --git a/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm b/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm index 6fad00d21f..d3883fa109 100644 --- a/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm +++ b/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm @@ -2518,7 +2518,7 @@ void GSDeviceMTL::SendHWDraw(GSHWDrawConfig& config, id for (u32 n = 0, p = 0; n < draw_list_size; n++) { - const size_t count = (*config.drawlist)[n] * indices_per_prim; + const size_t count = config.drawlist->at(n) * indices_per_prim; textureBarrier(enc); EncodeDraw(enc, topology, count, buffer, off, p); p += count; diff --git a/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp b/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp index 337d72e073..1acd198cc1 100644 --- a/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp +++ b/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp @@ -3033,6 +3033,57 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config) } } +void GSDeviceOGL::FeedbackCopyAndBind(const GSHWDrawConfig& config, + GSTexture* rt, GSTexture* rt_clone, GSTexture* ds, GSTexture* ds_clone, const GSVector4i& copyarea) +{ + if (rt_clone) + { + CopyRect(rt, rt_clone, copyarea, copyarea.left, copyarea.top); + PSSetShaderResource(2, rt_clone); + if (config.tex_hazard == GSHWDrawConfig::TEX_HAZARD_RT) + PSSetShaderResource(0, rt_clone); + } + if (ds_clone) + { + CopyRect(ds, ds_clone, copyarea, copyarea.left, copyarea.top); + PSSetShaderResource(4, ds_clone); + if (config.tex_hazard == GSHWDrawConfig::TEX_HAZARD_DEPTH) + PSSetShaderResource(0, ds_clone); + } +} + +// Choose the best copy area based on the hazards and whether we need RT and/or DS copies. +void GSDeviceOGL::FeedbackCopyAndBind(const GSHWDrawConfig& config, + GSTexture* rt, GSTexture* rt_clone, GSTexture* ds, GSTexture* ds_clone, + const GSVector4i& copyarea, const GSVector4i& samplearea) +{ + const GSVector4i rtsize = (rt ? rt : ds)->GetRect(); + + if (config.tex_hazard != GSHWDrawConfig::TEX_HAZARD_NONE) + { + const GSVector4i union_rect = config.drawarea.runion(config.samplearea); + const u32 size_union = union_rect.width() * union_rect.height(); + const u32 size_indiv = config.drawarea.width() * config.drawarea.height() + + config.samplearea.width() * config.samplearea.height(); + + // Do an individual copy if the union is larger than the sum of individual areas. + if (size_union > size_indiv) + { + FeedbackCopyAndBind(config, rt, rt_clone, ds, ds_clone, ProcessCopyArea(rtsize, config.drawarea)); + FeedbackCopyAndBind(config, rt, rt_clone, ds, ds_clone, ProcessCopyArea(rtsize, config.samplearea)); + } + else + { + FeedbackCopyAndBind(config, rt, rt_clone, ds, ds_clone, ProcessCopyArea(rtsize, union_rect)); + } + } + else + { + // No RT/DS hazards so just need the draw area. + FeedbackCopyAndBind(config, rt, rt_clone, ds, ds_clone, ProcessCopyArea(rtsize, config.drawarea)); + } +} + void GSDeviceOGL::SendHWDraw(const GSHWDrawConfig& config, GSTexture* draw_rt_clone, GSTexture* draw_rt, GSTexture* draw_ds_clone, GSTexture* draw_ds, const bool one_barrier, const bool full_barrier) @@ -3042,27 +3093,6 @@ void GSDeviceOGL::SendHWDraw(const GSHWDrawConfig& config, Console.Warning("OpenGL: Possible unnecessary barrier detected."); #endif - auto CopyAndBind = [&](GSVector4i drawarea) { - if (draw_rt_clone) - { - CopyRect(draw_rt, draw_rt_clone, drawarea, drawarea.left, drawarea.top); - if ((one_barrier || full_barrier)) - PSSetShaderResource(2, draw_rt_clone); - if (config.tex_hazard == GSHWDrawConfig::TEX_HAZARD_RT) - PSSetShaderResource(0, draw_rt_clone); - } - if (draw_ds_clone) - { - CopyRect(draw_ds, draw_ds_clone, drawarea, drawarea.left, drawarea.top); - if ((one_barrier || full_barrier)) - PSSetShaderResource(4, draw_ds_clone); - if (config.tex_hazard == GSHWDrawConfig::TEX_HAZARD_DEPTH) - PSSetShaderResource(0, draw_ds_clone); - } - }; - - const GSVector4i rtsize(0, 0, (draw_rt ? draw_rt : draw_ds)->GetWidth(), (draw_rt ? draw_rt : draw_ds)->GetHeight()); - if (full_barrier) { pxAssert(config.drawlist && !config.drawlist->empty()); @@ -3077,7 +3107,7 @@ void GSDeviceOGL::SendHWDraw(const GSHWDrawConfig& config, for (u32 n = 0, p = 0; n < draw_list_size; n++) { - const u32 count = (*config.drawlist)[n] * indices_per_prim; + const u32 count = config.drawlist->at(n) * indices_per_prim; if (m_features.texture_barrier) { @@ -3085,8 +3115,10 @@ void GSDeviceOGL::SendHWDraw(const GSHWDrawConfig& config, } else { - const GSVector4i original_bbox = (*config.drawlist_bbox)[n].rintersect(config.drawarea); - CopyAndBind(ProcessCopyArea(rtsize, original_bbox)); + const GSVector4i bbox = config.drawlist_bbox->at(n).rintersect(config.drawarea); + const GSVector4i bbox_tex = config.drawlist_bbox_tex ? + config.drawlist_bbox_tex->at(n).rintersect(config.samplearea) : GSVector4i::zero(); + FeedbackCopyAndBind(config, draw_rt, draw_rt_clone, draw_ds, draw_ds_clone, bbox, bbox_tex); } Draw(config, p, count); @@ -3106,28 +3138,7 @@ void GSDeviceOGL::SendHWDraw(const GSHWDrawConfig& config, else { // Optimization: For alpha second pass we can reuse the copy snapshot from the first pass. - if (config.tex_hazard) - { - const GSVector4i union_rect = config.drawarea.runion(config.samplearea); - const u32 size_union = union_rect.width() * union_rect.height(); - const u32 size_indiv = config.drawarea.width() * config.drawarea.height() + - config.samplearea.width() * config.samplearea.height(); - - // Do an individual copy if the union is larger than the sum of individual areas. - if (size_union > size_indiv) - { - CopyAndBind(ProcessCopyArea(rtsize, config.drawarea)); - CopyAndBind(ProcessCopyArea(rtsize, config.samplearea)); - } - else - { - CopyAndBind(ProcessCopyArea(rtsize, union_rect)); - } - } - else - { - CopyAndBind(ProcessCopyArea(rtsize, config.drawarea)); - } + FeedbackCopyAndBind(config, draw_rt, draw_rt_clone, draw_ds, draw_ds_clone, config.drawarea, config.samplearea); } } diff --git a/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.h b/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.h index dc3d84295d..0633c49288 100644 --- a/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.h +++ b/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.h @@ -352,6 +352,11 @@ public: void DoMultiStretchRects(const MultiStretchRect* rects, u32 num_rects, const GSVector2& ds); void RenderHW(GSHWDrawConfig& config) override; + void FeedbackCopyAndBind(const GSHWDrawConfig& config, + GSTexture* rt, GSTexture* rt_clone, GSTexture* ds, GSTexture* ds_clone, const GSVector4i& copyarea); + void FeedbackCopyAndBind(const GSHWDrawConfig& config, + GSTexture* rt, GSTexture* rt_clone, GSTexture* ds, GSTexture* ds_clone, + const GSVector4i& copyarea, const GSVector4i& samplearea); void SendHWDraw(const GSHWDrawConfig& config, GSTexture* draw_rt_clone, GSTexture* draw_rt, GSTexture* draw_ds_clone, GSTexture* draw_ds, const bool one_barrier, const bool full_barrier); diff --git a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp index 08a47ccbc2..f0a5a8cd51 100644 --- a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp +++ b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp @@ -6370,7 +6370,7 @@ void GSDeviceVK::SendHWDraw(const GSHWDrawConfig& config, GSTextureVK* draw_rt, { IssueBarriers(); - const u32 count = (*config.drawlist)[n] * indices_per_prim; + const u32 count = config.drawlist->at(n) * indices_per_prim; Draw(config, p, count); p += count; }