renderer_gl: Fix OpenGL texture unit state tracking (#2242)

* video_core: Implement texture-layer clears

Allow specific texture-layers to be targetted for clears rather than just defaulting to the first layer, or clearing all layers after in the case of Vulkan. Allows specific faces of a cubemap to be cleared.

* video_core: Initialize `NULL_SURFACE_CUBE_ID`

Properly initialize the `NULL_SURFACE_CUBE_ID` texture to ensure that all 6 of its faces are black-transparent.

* renderer_gl: Add explicit texture-unit target state tracking

The `.target` field can be either a `GL_TEXTURE_2D` or a
`GL_TEXTURE_CUBE_MAP` and must be explicitly set when assigning the
`.texture_2d` or there is a risk of leaking a previous texture state
into the current one in the case that a texture-cube was binded into
slot 0 and it becomes a texture-2d sometime after.
This commit is contained in:
Wunk 2026-07-01 08:25:03 -07:00 committed by GitHub
parent 7c225c6abb
commit d93adebb16
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 42 additions and 7 deletions

View File

@ -71,12 +71,28 @@ RasterizerCache<T>::RasterizerCache(Memory::MemorySystem& memory_,
auto& null_surface = slot_surfaces[NULL_SURFACE_ID];
runtime.ClearTexture(null_surface, {
.texture_level = 0,
.texture_layer = 0,
.texture_rect = null_surface.GetScaledRect(),
.value =
{
.color = {0.f, 0.f, 0.f, 0.f},
},
});
auto& null_surface_cube = slot_surfaces[NULL_SURFACE_CUBE_ID];
// Clear all cubemap faces
for (u32 cube_layer_index = 0; cube_layer_index < 6; ++cube_layer_index) {
runtime.ClearTexture(null_surface_cube,
{
.texture_level = 0,
.texture_layer = cube_layer_index,
.texture_rect = null_surface_cube.GetScaledRect(),
.value =
{
.color = {0.f, 0.f, 0.f, 0.f},
},
});
}
}
template <class T>

View File

@ -1,4 +1,4 @@
// Copyright 2023 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -30,6 +30,7 @@ union ClearValue {
struct TextureClear {
u32 texture_level;
u32 texture_layer;
Common::Rectangle<u32> texture_rect;
ClearValue value;
};

View File

@ -88,6 +88,7 @@ bool BlitHelper::ConvertDS24S8ToRGBA8(Surface& source, Surface& dest,
SCOPE_EXIT({ prev_state.Apply(); });
state.texture_units[0].texture_2d = source.Handle();
state.texture_units[0].target = GL_TEXTURE_2D;
state.texture_units[0].sampler = 0;
state.texture_units[1].sampler = 0;
@ -103,6 +104,7 @@ bool BlitHelper::ConvertDS24S8ToRGBA8(Surface& source, Surface& dest,
temp_tex.Release();
temp_tex.Create();
state.texture_units[1].texture_2d = temp_tex.handle;
state.texture_units[1].target = GL_TEXTURE_2D;
state.Apply();
glActiveTexture(GL_TEXTURE1);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH24_STENCIL8, temp_extent.width,
@ -111,6 +113,7 @@ bool BlitHelper::ConvertDS24S8ToRGBA8(Surface& source, Surface& dest,
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}
state.texture_units[1].texture_2d = temp_tex.handle;
state.texture_units[1].target = GL_TEXTURE_2D;
state.Apply();
glActiveTexture(GL_TEXTURE1);
@ -145,6 +148,7 @@ bool BlitHelper::ConvertRGBA4ToRGB5A1(Surface& source, Surface& dest,
SCOPE_EXIT({ prev_state.Apply(); });
state.texture_units[0].texture_2d = source.Handle();
state.texture_units[0].target = GL_TEXTURE_2D;
const Common::Rectangle src_rect{copy.src_offset.x, copy.src_offset.y + copy.extent.height,
copy.src_offset.x + copy.extent.width, copy.src_offset.x};
@ -206,6 +210,7 @@ void BlitHelper::FilterAnime4K(Surface& surface, const VideoCore::TextureBlit& b
texture.fbo.Create();
texture.tex.Create();
state.texture_units[1].texture_2d = texture.tex.handle;
state.texture_units[1].target = GL_TEXTURE_2D;
state.draw.draw_framebuffer = texture.fbo.handle;
state.Apply();
glActiveTexture(GL_TEXTURE1);
@ -228,6 +233,10 @@ void BlitHelper::FilterAnime4K(Surface& surface, const VideoCore::TextureBlit& b
state.texture_units[1].texture_2d = LUMAD.tex.handle;
state.texture_units[2].texture_2d = XY.tex.handle;
state.texture_units[0].target = GL_TEXTURE_2D;
state.texture_units[1].target = GL_TEXTURE_2D;
state.texture_units[2].target = GL_TEXTURE_2D;
// gradient x pass
Draw(gradient_x_program, XY.tex.handle, XY.fbo.handle, 0, temp_rect);
@ -249,6 +258,7 @@ void BlitHelper::FilterBicubic(Surface& surface, const VideoCore::TextureBlit& b
const OpenGLState prev_state = OpenGLState::GetCurState();
SCOPE_EXIT({ prev_state.Apply(); });
state.texture_units[0].texture_2d = surface.Handle(0);
state.texture_units[0].target = GL_TEXTURE_2D;
SetParams(bicubic_program, surface.RealExtent(false), blit.src_rect);
Draw(bicubic_program, surface.Handle(), draw_fbo.handle, blit.dst_level, blit.dst_rect);
}
@ -257,6 +267,7 @@ void BlitHelper::FilterScaleForce(Surface& surface, const VideoCore::TextureBlit
const OpenGLState prev_state = OpenGLState::GetCurState();
SCOPE_EXIT({ prev_state.Apply(); });
state.texture_units[0].texture_2d = surface.Handle(0);
state.texture_units[0].target = GL_TEXTURE_2D;
SetParams(scale_force_program, surface.RealExtent(false), blit.src_rect);
Draw(scale_force_program, surface.Handle(), draw_fbo.handle, blit.dst_level, blit.dst_rect);
}
@ -265,6 +276,7 @@ void BlitHelper::FilterXbrz(Surface& surface, const VideoCore::TextureBlit& blit
const OpenGLState prev_state = OpenGLState::GetCurState();
SCOPE_EXIT({ prev_state.Apply(); });
state.texture_units[0].texture_2d = surface.Handle(0);
state.texture_units[0].target = GL_TEXTURE_2D;
glProgramUniform1f(xbrz_program.handle, 2, static_cast<GLfloat>(surface.res_scale));
SetParams(xbrz_program, surface.RealExtent(false), blit.src_rect);
Draw(xbrz_program, surface.Handle(), draw_fbo.handle, blit.dst_level, blit.dst_rect);
@ -274,6 +286,7 @@ void BlitHelper::FilterMMPX(Surface& surface, const VideoCore::TextureBlit& blit
const OpenGLState prev_state = OpenGLState::GetCurState();
SCOPE_EXIT({ prev_state.Apply(); });
state.texture_units[0].texture_2d = surface.Handle(0);
state.texture_units[0].target = GL_TEXTURE_2D;
SetParams(mmpx_program, surface.RealExtent(false), blit.src_rect);
Draw(mmpx_program, surface.Handle(), draw_fbo.handle, blit.dst_level, blit.dst_rect);
}

View File

@ -717,6 +717,7 @@ void RasterizerOpenGL::SyncTextureUnits(const Framebuffer* framebuffer) {
if (!IsFeedbackLoop(texture_index, framebuffer, surface)) {
BindMaterial(texture_index, surface);
state.texture_units[texture_index].texture_2d = surface.Handle();
state.texture_units[texture_index].target = GL_TEXTURE_2D;
}
}

View File

@ -222,8 +222,9 @@ bool TextureRuntime::ClearTextureWithoutFbo(Surface& surface,
UNREACHABLE_MSG("Unknown surface type {}", surface.type);
}
glClearTexSubImage(surface.Handle(), clear.texture_level, clear.texture_rect.left,
clear.texture_rect.bottom, 0, clear.texture_rect.GetWidth(),
clear.texture_rect.GetHeight(), 1, format, type, &clear.value);
clear.texture_rect.bottom, clear.texture_layer,
clear.texture_rect.GetWidth(), clear.texture_rect.GetHeight(), 1, format,
type, &clear.value);
return true;
}
@ -245,7 +246,7 @@ void TextureRuntime::ClearTexture(Surface& surface, const VideoCore::TextureClea
state.draw.draw_framebuffer = draw_fbos[FboIndex(surface.type)].handle;
state.Apply();
surface.Attach(GL_DRAW_FRAMEBUFFER, clear.texture_level, 0);
surface.Attach(GL_DRAW_FRAMEBUFFER, clear.texture_level, clear.texture_layer);
switch (surface.type) {
case SurfaceType::Color:
@ -326,6 +327,7 @@ void TextureRuntime::GenerateMipmaps(Surface& surface) {
const auto generate = [&](u32 index) {
state.texture_units[0].texture_2d = surface.Handle(index);
state.texture_units[0].target = GL_TEXTURE_2D;
state.Apply();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, surface.levels - 1);
glGenerateMipmap(GL_TEXTURE_2D);
@ -554,6 +556,7 @@ bool Surface::DownloadWithoutFbo(const VideoCore::BufferTextureCopy& download,
// that only support up to 4.3
OpenGLState state = OpenGLState::GetCurState();
state.texture_units[0].texture_2d = Handle(0);
state.texture_units[0].target = GL_TEXTURE_2D;
state.Apply();
glGetTexImage(GL_TEXTURE_2D, download.texture_level, tuple.format, tuple.type,

View File

@ -350,8 +350,8 @@ bool TextureRuntime::ClearTexture(Surface& surface, const VideoCore::TextureClea
.aspectMask = params.aspect,
.baseMipLevel = clear.texture_level,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
.baseArrayLayer = clear.texture_layer,
.layerCount = 1,
};
const vk::ImageMemoryBarrier pre_barrier = {
@ -436,7 +436,8 @@ void TextureRuntime::ClearTextureWithRenderpass(Surface& surface,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = params.src_image,
.subresourceRange = MakeSubresourceRange(params.aspect, clear.texture_level),
.subresourceRange =
MakeSubresourceRange(params.aspect, clear.texture_level, 1, clear.texture_layer),
};
const vk::ImageMemoryBarrier post_barrier = {