mirror of
https://github.com/PabloMK7/citra.git
synced 2025-09-11 13:20:04 +00:00
Rasterizer cache refactor v2 (#6479)
* rasterizer_cache: Switch to template * Eliminates all opengl references in the rasterizer cache headers thus completing the backend abstraction * rasterizer_cache: Switch to page table * Surface storage isn't particularly interval sensitive so we can use a page table to make it faster * rasterizer_cache: Move sampler management out of rasterizer cache * rasterizer_cache: Remove shared_ptr usage * Switches to yuzu's slot vector for improved memory locality. * rasterizer_cache: Rework reinterpretation lookup * citra_qt: Per game texture filter * rasterizer_cache: Log additional settings * gl_texture_runtime: Resolve shadow map comment * rasterizer_cache: Don't use float for viewport * gl_texture_runtime: Fix custom allocation recycling * rasterizer_cache: Minor cleanups * Cleanup texture cubes when all the faces have been unregistered from the cache * custom_tex_manager: Allow multiple hash mappings per texture * code: Move slot vector to common * rasterizer_cache: Prevent texture cube crashes * rasterizer_cache: Improve mipmap validation * CanSubRect now works properly when validating multi-level surfaces, for example Dark Moon validates a 4 level surface from a 3 level one and it works * gl_blit_handler: Unbind sampler on reinterpretation
This commit is contained in:
parent
322d7a8287
commit
2e655f73b8
32 changed files with 2238 additions and 1927 deletions
|
@ -2,12 +2,16 @@
|
|||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/settings.h"
|
||||
#include "video_core/rasterizer_cache/pixel_format.h"
|
||||
#include "video_core/renderer_opengl/gl_blit_helper.h"
|
||||
#include "video_core/renderer_opengl/gl_driver.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/renderer_opengl/gl_texture_runtime.h"
|
||||
|
||||
#include "video_core/host_shaders/format_reinterpreter/d24s8_to_rgba8_frag.h"
|
||||
#include "video_core/host_shaders/format_reinterpreter/rgba4_to_rgb5a1_frag.h"
|
||||
#include "video_core/host_shaders/full_screen_triangle_vert.h"
|
||||
#include "video_core/host_shaders/texture_filtering/bicubic_frag.h"
|
||||
#include "video_core/host_shaders/texture_filtering/nearest_neighbor_frag.h"
|
||||
|
@ -49,8 +53,8 @@ OGLProgram CreateProgram(std::string_view frag) {
|
|||
|
||||
} // Anonymous namespace
|
||||
|
||||
BlitHelper::BlitHelper(TextureRuntime& runtime_)
|
||||
: runtime{runtime_}, linear_sampler{CreateSampler(GL_LINEAR)},
|
||||
BlitHelper::BlitHelper(const Driver& driver_)
|
||||
: driver{driver_}, linear_sampler{CreateSampler(GL_LINEAR)},
|
||||
nearest_sampler{CreateSampler(GL_NEAREST)}, bicubic_program{CreateProgram(
|
||||
HostShaders::BICUBIC_FRAG)},
|
||||
nearest_program{CreateProgram(HostShaders::NEAREST_NEIGHBOR_FRAG)},
|
||||
|
@ -58,34 +62,104 @@ BlitHelper::BlitHelper(TextureRuntime& runtime_)
|
|||
xbrz_program{CreateProgram(HostShaders::XBRZ_FREESCALE_FRAG)},
|
||||
gradient_x_program{CreateProgram(HostShaders::X_GRADIENT_FRAG)},
|
||||
gradient_y_program{CreateProgram(HostShaders::Y_GRADIENT_FRAG)},
|
||||
refine_program{CreateProgram(HostShaders::REFINE_FRAG)} {
|
||||
refine_program{CreateProgram(HostShaders::REFINE_FRAG)},
|
||||
d24s8_to_rgba8{CreateProgram(HostShaders::D24S8_TO_RGBA8_FRAG)},
|
||||
rgba4_to_rgb5a1{CreateProgram(HostShaders::RGBA4_TO_RGB5A1_FRAG)} {
|
||||
vao.Create();
|
||||
filter_fbo.Create();
|
||||
draw_fbo.Create();
|
||||
state.draw.vertex_array = vao.handle;
|
||||
for (u32 i = 0; i < 3; i++) {
|
||||
state.texture_units[i].sampler = i == 2 ? nearest_sampler.handle : linear_sampler.handle;
|
||||
}
|
||||
if (driver.IsOpenGLES()) {
|
||||
LOG_INFO(Render_OpenGL,
|
||||
"Texture views are unsupported, reinterpretation will do intermediate copy");
|
||||
temp_tex.Create();
|
||||
use_texture_view = false;
|
||||
}
|
||||
}
|
||||
|
||||
BlitHelper::~BlitHelper() = default;
|
||||
|
||||
bool BlitHelper::ConvertDS24S8ToRGBA8(Surface& source, Surface& dest,
|
||||
const VideoCore::TextureBlit& blit) {
|
||||
OpenGLState prev_state = OpenGLState::GetCurState();
|
||||
SCOPE_EXIT({ prev_state.Apply(); });
|
||||
|
||||
state.texture_units[0].texture_2d = source.Handle();
|
||||
state.texture_units[0].sampler = 0;
|
||||
state.texture_units[1].sampler = 0;
|
||||
|
||||
if (use_texture_view) {
|
||||
temp_tex.Create();
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glTextureView(temp_tex.handle, GL_TEXTURE_2D, source.Handle(), GL_DEPTH24_STENCIL8, 0, 1, 0,
|
||||
1);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
} else if (blit.src_rect.top > temp_rect.top || blit.src_rect.right > temp_rect.right) {
|
||||
temp_tex.Release();
|
||||
temp_tex.Create();
|
||||
state.texture_units[1].texture_2d = temp_tex.handle;
|
||||
state.Apply();
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH24_STENCIL8, blit.src_rect.right,
|
||||
blit.src_rect.top);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
temp_rect = blit.src_rect;
|
||||
}
|
||||
state.texture_units[1].texture_2d = temp_tex.handle;
|
||||
state.Apply();
|
||||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
if (!use_texture_view) {
|
||||
glCopyImageSubData(source.Handle(), GL_TEXTURE_2D, 0, blit.src_rect.left,
|
||||
blit.src_rect.bottom, 0, temp_tex.handle, GL_TEXTURE_2D, 0,
|
||||
blit.src_rect.left, blit.src_rect.bottom, 0, blit.src_rect.GetWidth(),
|
||||
blit.src_rect.GetHeight(), 1);
|
||||
}
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_STENCIL_INDEX);
|
||||
|
||||
SetParams(d24s8_to_rgba8, source.RealExtent(), blit.src_rect);
|
||||
Draw(d24s8_to_rgba8, dest.Handle(), draw_fbo.handle, 0, blit.dst_rect);
|
||||
|
||||
if (use_texture_view) {
|
||||
temp_tex.Release();
|
||||
}
|
||||
|
||||
// Restore the sampler handles
|
||||
state.texture_units[0].sampler = linear_sampler.handle;
|
||||
state.texture_units[1].sampler = linear_sampler.handle;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BlitHelper::ConvertRGBA4ToRGB5A1(Surface& source, Surface& dest,
|
||||
const VideoCore::TextureBlit& blit) {
|
||||
OpenGLState prev_state = OpenGLState::GetCurState();
|
||||
SCOPE_EXIT({ prev_state.Apply(); });
|
||||
|
||||
state.texture_units[0].texture_2d = source.Handle();
|
||||
|
||||
SetParams(rgba4_to_rgb5a1, source.RealExtent(), blit.src_rect);
|
||||
Draw(rgba4_to_rgb5a1, dest.Handle(), draw_fbo.handle, 0, blit.dst_rect);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BlitHelper::Filter(Surface& surface, const VideoCore::TextureBlit& blit) {
|
||||
// Filtering to depth stencil surfaces isn't supported.
|
||||
if (surface.type == SurfaceType::Depth || surface.type == SurfaceType::DepthStencil) {
|
||||
const auto filter = Settings::values.texture_filter.GetValue();
|
||||
const bool is_depth =
|
||||
surface.type == SurfaceType::Depth || surface.type == SurfaceType::DepthStencil;
|
||||
if (filter == Settings::TextureFilter::None || is_depth) {
|
||||
return false;
|
||||
}
|
||||
// Avoid filtering for mipmaps as the result often looks terrible.
|
||||
if (blit.src_level != 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const OpenGLState prev_state = OpenGLState::GetCurState();
|
||||
state.texture_units[0].texture_2d = surface.Handle(0);
|
||||
|
||||
const auto filter{Settings::values.texture_filter.GetValue()};
|
||||
switch (filter) {
|
||||
case TextureFilter::None:
|
||||
break;
|
||||
case TextureFilter::Anime4K:
|
||||
FilterAnime4K(surface, blit);
|
||||
break;
|
||||
|
@ -101,15 +175,19 @@ bool BlitHelper::Filter(Surface& surface, const VideoCore::TextureBlit& blit) {
|
|||
case TextureFilter::xBRZ:
|
||||
FilterXbrz(surface, blit);
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Render_OpenGL, "Unknown texture filter {}", filter);
|
||||
}
|
||||
|
||||
prev_state.Apply();
|
||||
return true;
|
||||
}
|
||||
|
||||
void BlitHelper::FilterAnime4K(Surface& surface, const VideoCore::TextureBlit& blit) {
|
||||
static constexpr u8 internal_scale_factor = 2;
|
||||
|
||||
const OpenGLState prev_state = OpenGLState::GetCurState();
|
||||
SCOPE_EXIT({ prev_state.Apply(); });
|
||||
|
||||
const auto& tuple = surface.Tuple();
|
||||
const u32 src_width = blit.src_rect.GetWidth();
|
||||
const u32 src_height = blit.src_rect.GetHeight();
|
||||
|
@ -149,7 +227,7 @@ void BlitHelper::FilterAnime4K(Surface& surface, const VideoCore::TextureBlit& b
|
|||
Draw(gradient_y_program, LUMAD.tex.handle, LUMAD.fbo.handle, 0, temp_rect);
|
||||
|
||||
// refine pass
|
||||
Draw(refine_program, surface.Handle(), filter_fbo.handle, blit.dst_level, blit.dst_rect);
|
||||
Draw(refine_program, surface.Handle(), draw_fbo.handle, blit.dst_level, blit.dst_rect);
|
||||
|
||||
// These will have handles from the previous texture that was filtered, reset them to avoid
|
||||
// binding invalid textures.
|
||||
|
@ -160,25 +238,36 @@ void BlitHelper::FilterAnime4K(Surface& surface, const VideoCore::TextureBlit& b
|
|||
}
|
||||
|
||||
void BlitHelper::FilterBicubic(Surface& surface, const VideoCore::TextureBlit& blit) {
|
||||
SetParams(bicubic_program, surface.Extent(), blit.src_rect);
|
||||
Draw(bicubic_program, surface.Handle(), filter_fbo.handle, blit.dst_level, blit.dst_rect);
|
||||
const OpenGLState prev_state = OpenGLState::GetCurState();
|
||||
SCOPE_EXIT({ prev_state.Apply(); });
|
||||
state.texture_units[0].texture_2d = surface.Handle(0);
|
||||
SetParams(bicubic_program, surface.RealExtent(false), blit.src_rect);
|
||||
Draw(bicubic_program, surface.Handle(), draw_fbo.handle, blit.dst_level, blit.dst_rect);
|
||||
}
|
||||
|
||||
void BlitHelper::FilterNearest(Surface& surface, const VideoCore::TextureBlit& blit) {
|
||||
const OpenGLState prev_state = OpenGLState::GetCurState();
|
||||
SCOPE_EXIT({ prev_state.Apply(); });
|
||||
state.texture_units[2].texture_2d = surface.Handle(0);
|
||||
SetParams(nearest_program, surface.Extent(), blit.src_rect);
|
||||
Draw(nearest_program, surface.Handle(), filter_fbo.handle, blit.dst_level, blit.dst_rect);
|
||||
SetParams(nearest_program, surface.RealExtent(false), blit.src_rect);
|
||||
Draw(nearest_program, surface.Handle(), draw_fbo.handle, blit.dst_level, blit.dst_rect);
|
||||
}
|
||||
|
||||
void BlitHelper::FilterScaleForce(Surface& surface, const VideoCore::TextureBlit& blit) {
|
||||
SetParams(scale_force_program, surface.Extent(), blit.src_rect);
|
||||
Draw(scale_force_program, surface.Handle(), filter_fbo.handle, blit.dst_level, blit.dst_rect);
|
||||
const OpenGLState prev_state = OpenGLState::GetCurState();
|
||||
SCOPE_EXIT({ prev_state.Apply(); });
|
||||
state.texture_units[0].texture_2d = surface.Handle(0);
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
glProgramUniform1f(xbrz_program.handle, 2, static_cast<GLfloat>(surface.res_scale));
|
||||
SetParams(xbrz_program, surface.Extent(), blit.src_rect);
|
||||
Draw(xbrz_program, surface.Handle(), filter_fbo.handle, blit.dst_level, blit.dst_rect);
|
||||
SetParams(xbrz_program, surface.RealExtent(false), blit.src_rect);
|
||||
Draw(xbrz_program, surface.Handle(), draw_fbo.handle, blit.dst_level, blit.dst_rect);
|
||||
}
|
||||
|
||||
void BlitHelper::SetParams(OGLProgram& program, const VideoCore::Extent& src_extent,
|
||||
|
@ -206,7 +295,7 @@ void BlitHelper::Draw(OGLProgram& program, GLuint dst_tex, GLuint dst_fbo, u32 d
|
|||
dst_level);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
||||
|
|
|
@ -15,16 +15,20 @@ struct TextureBlit;
|
|||
|
||||
namespace OpenGL {
|
||||
|
||||
class TextureRuntime;
|
||||
class Driver;
|
||||
class Surface;
|
||||
|
||||
class BlitHelper {
|
||||
public:
|
||||
BlitHelper(TextureRuntime& runtime);
|
||||
explicit BlitHelper(const Driver& driver);
|
||||
~BlitHelper();
|
||||
|
||||
bool Filter(Surface& surface, const VideoCore::TextureBlit& blit);
|
||||
|
||||
bool ConvertDS24S8ToRGBA8(Surface& source, Surface& dest, const VideoCore::TextureBlit& blit);
|
||||
|
||||
bool ConvertRGBA4ToRGB5A1(Surface& source, Surface& dest, const VideoCore::TextureBlit& blit);
|
||||
|
||||
private:
|
||||
void FilterAnime4K(Surface& surface, const VideoCore::TextureBlit& blit);
|
||||
|
||||
|
@ -43,10 +47,10 @@ private:
|
|||
Common::Rectangle<u32> dst_rect);
|
||||
|
||||
private:
|
||||
TextureRuntime& runtime;
|
||||
const Driver& driver;
|
||||
OGLVertexArray vao;
|
||||
OpenGLState state;
|
||||
OGLFramebuffer filter_fbo;
|
||||
OGLFramebuffer draw_fbo;
|
||||
OGLSampler linear_sampler;
|
||||
OGLSampler nearest_sampler;
|
||||
|
||||
|
@ -57,6 +61,12 @@ private:
|
|||
OGLProgram gradient_x_program;
|
||||
OGLProgram gradient_y_program;
|
||||
OGLProgram refine_program;
|
||||
OGLProgram d24s8_to_rgba8;
|
||||
OGLProgram rgba4_to_rgb5a1;
|
||||
|
||||
OGLTexture temp_tex;
|
||||
Common::Rectangle<u32> temp_rect{};
|
||||
bool use_texture_view{true};
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
|
|
|
@ -1,134 +0,0 @@
|
|||
// Copyright 2022 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/scope_exit.h"
|
||||
#include "video_core/renderer_opengl/gl_format_reinterpreter.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/renderer_opengl/gl_texture_runtime.h"
|
||||
|
||||
#include "video_core/host_shaders/format_reinterpreter/d24s8_to_rgba8_frag.h"
|
||||
#include "video_core/host_shaders/format_reinterpreter/fullscreen_quad_vert.h"
|
||||
#include "video_core/host_shaders/format_reinterpreter/rgba4_to_rgb5a1_frag.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
RGBA4toRGB5A1::RGBA4toRGB5A1() {
|
||||
program.Create(HostShaders::FULLSCREEN_QUAD_VERT, HostShaders::RGBA4_TO_RGB5A1_FRAG);
|
||||
dst_size_loc = glGetUniformLocation(program.handle, "dst_size");
|
||||
src_size_loc = glGetUniformLocation(program.handle, "src_size");
|
||||
src_offset_loc = glGetUniformLocation(program.handle, "src_offset");
|
||||
vao.Create();
|
||||
}
|
||||
|
||||
void RGBA4toRGB5A1::Reinterpret(Surface& source, Common::Rectangle<u32> src_rect, Surface& dest,
|
||||
Common::Rectangle<u32> dst_rect) {
|
||||
OpenGLState prev_state = OpenGLState::GetCurState();
|
||||
SCOPE_EXIT({ prev_state.Apply(); });
|
||||
|
||||
OpenGLState state;
|
||||
state.texture_units[0].texture_2d = source.Handle();
|
||||
state.draw.draw_framebuffer = draw_fbo.handle;
|
||||
state.draw.shader_program = program.handle;
|
||||
state.draw.vertex_array = vao.handle;
|
||||
state.viewport = {static_cast<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.bottom),
|
||||
static_cast<GLsizei>(dst_rect.GetWidth()),
|
||||
static_cast<GLsizei>(dst_rect.GetHeight())};
|
||||
state.Apply();
|
||||
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dest.Handle(),
|
||||
0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
|
||||
|
||||
glUniform2i(dst_size_loc, dst_rect.GetWidth(), dst_rect.GetHeight());
|
||||
glUniform2i(src_size_loc, src_rect.GetWidth(), src_rect.GetHeight());
|
||||
glUniform2i(src_offset_loc, src_rect.left, src_rect.bottom);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
|
||||
ShaderD24S8toRGBA8::ShaderD24S8toRGBA8() {
|
||||
program.Create(HostShaders::FULLSCREEN_QUAD_VERT, HostShaders::D24S8_TO_RGBA8_FRAG);
|
||||
dst_size_loc = glGetUniformLocation(program.handle, "dst_size");
|
||||
src_size_loc = glGetUniformLocation(program.handle, "src_size");
|
||||
src_offset_loc = glGetUniformLocation(program.handle, "src_offset");
|
||||
vao.Create();
|
||||
|
||||
auto state = OpenGLState::GetCurState();
|
||||
auto cur_program = state.draw.shader_program;
|
||||
state.draw.shader_program = program.handle;
|
||||
state.Apply();
|
||||
glUniform1i(glGetUniformLocation(program.handle, "stencil"), 1);
|
||||
state.draw.shader_program = cur_program;
|
||||
state.Apply();
|
||||
|
||||
// Nvidia seem to be the only one to support D24S8 views, at least on windows
|
||||
// so for everyone else it will do an intermediate copy before running through the shader
|
||||
std::string_view vendor{reinterpret_cast<const char*>(glGetString(GL_VENDOR))};
|
||||
if (vendor.find("NVIDIA") != vendor.npos) {
|
||||
use_texture_view = true;
|
||||
} else {
|
||||
LOG_INFO(Render_OpenGL,
|
||||
"Texture views are unsupported, reinterpretation will do intermediate copy");
|
||||
temp_tex.Create();
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderD24S8toRGBA8::Reinterpret(Surface& source, Common::Rectangle<u32> src_rect,
|
||||
Surface& dest, Common::Rectangle<u32> dst_rect) {
|
||||
OpenGLState prev_state = OpenGLState::GetCurState();
|
||||
SCOPE_EXIT({ prev_state.Apply(); });
|
||||
|
||||
OpenGLState state;
|
||||
state.texture_units[0].texture_2d = source.Handle();
|
||||
|
||||
if (use_texture_view) {
|
||||
temp_tex.Create();
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glTextureView(temp_tex.handle, GL_TEXTURE_2D, source.Handle(), GL_DEPTH24_STENCIL8, 0, 1, 0,
|
||||
1);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
} else if (src_rect.top > temp_rect.top || src_rect.right > temp_rect.right) {
|
||||
temp_tex.Release();
|
||||
temp_tex.Create();
|
||||
state.texture_units[1].texture_2d = temp_tex.handle;
|
||||
state.Apply();
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH24_STENCIL8, src_rect.right, src_rect.top);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
temp_rect = src_rect;
|
||||
}
|
||||
|
||||
state.texture_units[1].texture_2d = temp_tex.handle;
|
||||
state.draw.draw_framebuffer = draw_fbo.handle;
|
||||
state.draw.shader_program = program.handle;
|
||||
state.draw.vertex_array = vao.handle;
|
||||
state.viewport = {static_cast<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.bottom),
|
||||
static_cast<GLsizei>(dst_rect.GetWidth()),
|
||||
static_cast<GLsizei>(dst_rect.GetHeight())};
|
||||
state.Apply();
|
||||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
if (!use_texture_view) {
|
||||
glCopyImageSubData(source.Handle(), GL_TEXTURE_2D, 0, src_rect.left, src_rect.bottom, 0,
|
||||
temp_tex.handle, GL_TEXTURE_2D, 0, src_rect.left, src_rect.bottom, 0,
|
||||
src_rect.GetWidth(), src_rect.GetHeight(), 1);
|
||||
}
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_STENCIL_INDEX);
|
||||
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dest.Handle(),
|
||||
0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
|
||||
|
||||
glUniform2i(dst_size_loc, dst_rect.GetWidth(), dst_rect.GetHeight());
|
||||
glUniform2i(src_size_loc, src_rect.GetWidth(), src_rect.GetHeight());
|
||||
glUniform2i(src_offset_loc, src_rect.left, src_rect.bottom);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
if (use_texture_view) {
|
||||
temp_tex.Release();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
|
@ -1,76 +0,0 @@
|
|||
// Copyright 2022 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/math_util.h"
|
||||
#include "video_core/rasterizer_cache/pixel_format.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
class Surface;
|
||||
|
||||
class FormatReinterpreterBase {
|
||||
public:
|
||||
FormatReinterpreterBase() {
|
||||
read_fbo.Create();
|
||||
draw_fbo.Create();
|
||||
}
|
||||
|
||||
virtual ~FormatReinterpreterBase() = default;
|
||||
|
||||
virtual VideoCore::PixelFormat GetSourceFormat() const = 0;
|
||||
virtual void Reinterpret(Surface& source, Common::Rectangle<u32> src_rect, Surface& dest,
|
||||
Common::Rectangle<u32> dst_rect) = 0;
|
||||
|
||||
protected:
|
||||
OGLFramebuffer read_fbo;
|
||||
OGLFramebuffer draw_fbo;
|
||||
};
|
||||
|
||||
using ReinterpreterList = std::vector<std::unique_ptr<FormatReinterpreterBase>>;
|
||||
|
||||
class RGBA4toRGB5A1 final : public FormatReinterpreterBase {
|
||||
public:
|
||||
RGBA4toRGB5A1();
|
||||
|
||||
VideoCore::PixelFormat GetSourceFormat() const override {
|
||||
return VideoCore::PixelFormat::RGBA4;
|
||||
}
|
||||
|
||||
void Reinterpret(Surface& source, Common::Rectangle<u32> src_rect, Surface& dest,
|
||||
Common::Rectangle<u32> dst_rect) override;
|
||||
|
||||
private:
|
||||
OGLProgram program;
|
||||
GLint dst_size_loc{-1};
|
||||
GLint src_size_loc{-1};
|
||||
GLint src_offset_loc{-1};
|
||||
OGLVertexArray vao;
|
||||
};
|
||||
|
||||
class ShaderD24S8toRGBA8 final : public FormatReinterpreterBase {
|
||||
public:
|
||||
ShaderD24S8toRGBA8();
|
||||
|
||||
VideoCore::PixelFormat GetSourceFormat() const override {
|
||||
return VideoCore::PixelFormat::D24S8;
|
||||
}
|
||||
|
||||
void Reinterpret(Surface& source, Common::Rectangle<u32> src_rect, Surface& dest,
|
||||
Common::Rectangle<u32> dst_rect) override;
|
||||
|
||||
private:
|
||||
bool use_texture_view{};
|
||||
OGLProgram program{};
|
||||
GLint dst_size_loc{-1};
|
||||
GLint src_size_loc{-1};
|
||||
GLint src_offset_loc{-1};
|
||||
OGLVertexArray vao{};
|
||||
OGLTexture temp_tex{};
|
||||
Common::Rectangle<u32> temp_rect{0, 0, 0, 0};
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
|
@ -25,7 +25,7 @@ MICROPROFILE_DEFINE(OpenGL_VAO, "OpenGL", "Vertex Array Setup", MP_RGB(255, 128,
|
|||
MICROPROFILE_DEFINE(OpenGL_VS, "OpenGL", "Vertex Shader Setup", MP_RGB(192, 128, 128));
|
||||
MICROPROFILE_DEFINE(OpenGL_GS, "OpenGL", "Geometry Shader Setup", MP_RGB(128, 192, 128));
|
||||
MICROPROFILE_DEFINE(OpenGL_Drawing, "OpenGL", "Drawing", MP_RGB(128, 128, 192));
|
||||
MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100));
|
||||
MICROPROFILE_DEFINE(OpenGL_Display, "OpenGL", "Display", MP_RGB(128, 128, 192));
|
||||
|
||||
using VideoCore::SurfaceType;
|
||||
|
||||
|
@ -97,16 +97,6 @@ RasterizerOpenGL::RasterizerOpenGL(Memory::MemorySystem& memory,
|
|||
u8 framebuffer_data[4] = {0, 0, 0, 1};
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, framebuffer_data);
|
||||
|
||||
// Create sampler objects
|
||||
for (std::size_t i = 0; i < texture_samplers.size(); ++i) {
|
||||
texture_samplers[i].Create();
|
||||
state.texture_units[i].sampler = texture_samplers[i].sampler.handle;
|
||||
}
|
||||
|
||||
// Create cubemap texture and sampler objects
|
||||
texture_cube_sampler.Create();
|
||||
state.texture_cube_unit.sampler = texture_cube_sampler.sampler.handle;
|
||||
|
||||
// Generate VAO
|
||||
sw_vao.Create();
|
||||
hw_vao.Create();
|
||||
|
@ -251,14 +241,14 @@ void RasterizerOpenGL::SetupVertexArray(u8* array_ptr, GLintptr buffer_offset,
|
|||
}
|
||||
}
|
||||
|
||||
PAddr data_addr =
|
||||
const PAddr data_addr =
|
||||
base_address + loader.data_offset + (vs_input_index_min * loader.byte_count);
|
||||
|
||||
u32 vertex_num = vs_input_index_max - vs_input_index_min + 1;
|
||||
u32 data_size = loader.byte_count * vertex_num;
|
||||
const u32 vertex_num = vs_input_index_max - vs_input_index_min + 1;
|
||||
const u32 data_size = loader.byte_count * vertex_num;
|
||||
|
||||
res_cache.FlushRegion(data_addr, data_size, nullptr);
|
||||
std::memcpy(array_ptr, VideoCore::g_memory->GetPhysicalPointer(data_addr), data_size);
|
||||
res_cache.FlushRegion(data_addr, data_size);
|
||||
std::memcpy(array_ptr, memory.GetPhysicalPointer(data_addr), data_size);
|
||||
|
||||
array_ptr += data_size;
|
||||
buffer_offset += data_size;
|
||||
|
@ -287,8 +277,7 @@ void RasterizerOpenGL::SetupVertexArray(u8* array_ptr, GLintptr buffer_offset,
|
|||
|
||||
bool RasterizerOpenGL::SetupVertexShader() {
|
||||
MICROPROFILE_SCOPE(OpenGL_VS);
|
||||
return shader_program_manager->UseProgrammableVertexShader(Pica::g_state.regs,
|
||||
Pica::g_state.vs);
|
||||
return shader_program_manager->UseProgrammableVertexShader(regs, Pica::g_state.vs);
|
||||
}
|
||||
|
||||
bool RasterizerOpenGL::SetupGeometryShader() {
|
||||
|
@ -400,8 +389,7 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) {
|
|||
const Framebuffer framebuffer =
|
||||
res_cache.GetFramebufferSurfaces(using_color_fb, using_depth_fb);
|
||||
const bool has_color = framebuffer.HasAttachment(SurfaceType::Color);
|
||||
const bool has_depth_stencil = framebuffer.HasAttachment(SurfaceType::DepthStencil);
|
||||
if (!has_color && (shadow_rendering || !has_depth_stencil)) {
|
||||
if (!has_color && shadow_rendering) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -520,8 +508,9 @@ void RasterizerOpenGL::SyncTextureUnits(const Framebuffer& framebuffer) {
|
|||
if (texture_index == 0) {
|
||||
switch (texture.config.type.Value()) {
|
||||
case TextureType::Shadow2D: {
|
||||
auto surface = res_cache.GetTextureSurface(texture);
|
||||
state.image_shadow_texture_px = surface->Handle();
|
||||
Surface& surface = res_cache.GetTextureSurface(texture);
|
||||
surface.flags |= VideoCore::SurfaceFlagBits::ShadowMap;
|
||||
state.image_shadow_texture_px = surface.Handle();
|
||||
continue;
|
||||
}
|
||||
case TextureType::ShadowCube: {
|
||||
|
@ -538,22 +527,14 @@ void RasterizerOpenGL::SyncTextureUnits(const Framebuffer& framebuffer) {
|
|||
}
|
||||
|
||||
// Sync texture unit sampler
|
||||
texture_samplers[texture_index].SyncWithConfig(texture.config);
|
||||
Sampler& sampler = res_cache.GetSampler(texture.config);
|
||||
state.texture_units[texture_index].sampler = sampler.Handle();
|
||||
|
||||
// Bind the texture provided by the rasterizer cache
|
||||
auto surface = res_cache.GetTextureSurface(texture);
|
||||
if (!surface) {
|
||||
// Can occur when texture addr is null or its memory is unmapped/invalid
|
||||
// HACK: In this case, the correct behaviour for the PICA is to use the last
|
||||
// rendered colour. But because this would be impractical to implement, the
|
||||
// next best alternative is to use a clear texture, essentially skipping
|
||||
// the geometry in question.
|
||||
// For example: a bug in Pokemon X/Y causes NULL-texture squares to be drawn
|
||||
// on the male character's face, which in the OpenGL default appear black.
|
||||
state.texture_units[texture_index].texture_2d = default_texture;
|
||||
} else if (!IsFeedbackLoop(texture_index, framebuffer, *surface)) {
|
||||
BindMaterial(texture_index, *surface);
|
||||
state.texture_units[texture_index].texture_2d = surface->Handle();
|
||||
Surface& surface = res_cache.GetTextureSurface(texture);
|
||||
if (!IsFeedbackLoop(texture_index, framebuffer, surface)) {
|
||||
BindMaterial(texture_index, surface);
|
||||
state.texture_units[texture_index].texture_2d = surface.Handle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -570,8 +551,10 @@ void RasterizerOpenGL::BindShadowCube(const Pica::TexturingRegs::FullTextureConf
|
|||
const u32 binding = static_cast<u32>(face);
|
||||
info.physical_address = regs.texturing.GetCubePhysicalAddress(face);
|
||||
|
||||
auto surface = res_cache.GetTextureSurface(info);
|
||||
state.image_shadow_texture[binding] = surface->Handle();
|
||||
VideoCore::SurfaceId surface_id = res_cache.GetTextureSurface(info);
|
||||
Surface& surface = res_cache.GetSurface(surface_id);
|
||||
surface.flags |= VideoCore::SurfaceFlagBits::ShadowMap;
|
||||
state.image_shadow_texture[binding] = surface.Handle();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -589,10 +572,11 @@ void RasterizerOpenGL::BindTextureCube(const Pica::TexturingRegs::FullTextureCon
|
|||
.format = texture.format,
|
||||
};
|
||||
|
||||
auto surface = res_cache.GetTextureCube(config);
|
||||
texture_cube_sampler.SyncWithConfig(texture.config);
|
||||
Surface& surface = res_cache.GetTextureCube(config);
|
||||
Sampler& sampler = res_cache.GetSampler(texture.config);
|
||||
|
||||
state.texture_cube_unit.texture_cube = surface->Handle();
|
||||
state.texture_cube_unit.texture_cube = surface.Handle();
|
||||
state.texture_cube_unit.sampler = sampler.Handle();
|
||||
state.texture_units[0].texture_2d = 0;
|
||||
}
|
||||
|
||||
|
@ -608,7 +592,7 @@ void RasterizerOpenGL::BindMaterial(u32 texture_index, Surface& surface) {
|
|||
glBindSampler(unit.id, sampler);
|
||||
};
|
||||
|
||||
const GLuint sampler = texture_samplers[texture_index].sampler.handle;
|
||||
const GLuint sampler = state.texture_units[texture_index].sampler;
|
||||
if (surface.HasNormalMap()) {
|
||||
if (regs.lighting.disable) {
|
||||
LOG_WARNING(Render_OpenGL, "Custom normal map used but scene has no light enabled");
|
||||
|
@ -726,24 +710,20 @@ void RasterizerOpenGL::NotifyFixedFunctionPicaRegisterChanged(u32 id) {
|
|||
}
|
||||
|
||||
void RasterizerOpenGL::FlushAll() {
|
||||
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
|
||||
res_cache.FlushAll();
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::FlushRegion(PAddr addr, u32 size) {
|
||||
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
|
||||
res_cache.FlushRegion(addr, size);
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::InvalidateRegion(PAddr addr, u32 size) {
|
||||
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
|
||||
res_cache.InvalidateRegion(addr, size, nullptr);
|
||||
res_cache.InvalidateRegion(addr, size);
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::FlushAndInvalidateRegion(PAddr addr, u32 size) {
|
||||
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
|
||||
res_cache.FlushRegion(addr, size);
|
||||
res_cache.InvalidateRegion(addr, size, nullptr);
|
||||
res_cache.InvalidateRegion(addr, size);
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::ClearAll(bool flush) {
|
||||
|
@ -768,7 +748,7 @@ bool RasterizerOpenGL::AccelerateDisplay(const GPU::Regs::FramebufferConfig& con
|
|||
if (framebuffer_addr == 0) {
|
||||
return false;
|
||||
}
|
||||
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
|
||||
MICROPROFILE_SCOPE(OpenGL_Display);
|
||||
|
||||
VideoCore::SurfaceParams src_params;
|
||||
src_params.addr = framebuffer_addr;
|
||||
|
@ -779,85 +759,27 @@ bool RasterizerOpenGL::AccelerateDisplay(const GPU::Regs::FramebufferConfig& con
|
|||
src_params.pixel_format = VideoCore::PixelFormatFromGPUPixelFormat(config.color_format);
|
||||
src_params.UpdateParams();
|
||||
|
||||
auto [src_surface, src_rect] =
|
||||
const auto [src_surface_id, src_rect] =
|
||||
res_cache.GetSurfaceSubRect(src_params, VideoCore::ScaleMatch::Ignore, true);
|
||||
|
||||
if (src_surface == nullptr) {
|
||||
if (!src_surface_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const u32 scaled_width = src_surface->GetScaledWidth();
|
||||
const u32 scaled_height = src_surface->GetScaledHeight();
|
||||
const Surface& src_surface = res_cache.GetSurface(src_surface_id);
|
||||
const u32 scaled_width = src_surface.GetScaledWidth();
|
||||
const u32 scaled_height = src_surface.GetScaledHeight();
|
||||
|
||||
screen_info.display_texcoords = Common::Rectangle<float>(
|
||||
(float)src_rect.bottom / (float)scaled_height, (float)src_rect.left / (float)scaled_width,
|
||||
(float)src_rect.top / (float)scaled_height, (float)src_rect.right / (float)scaled_width);
|
||||
|
||||
screen_info.display_texture = src_surface->Handle();
|
||||
screen_info.display_texture = src_surface.Handle();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SamplerInfo::Create() {
|
||||
sampler.Create();
|
||||
mag_filter = min_filter = mip_filter = TextureConfig::Linear;
|
||||
wrap_s = wrap_t = TextureConfig::Repeat;
|
||||
border_color = 0;
|
||||
lod_min = lod_max = 0;
|
||||
|
||||
// default is 1000 and -1000
|
||||
// Other attributes have correct defaults
|
||||
glSamplerParameterf(sampler.handle, GL_TEXTURE_MAX_LOD, static_cast<float>(lod_max));
|
||||
glSamplerParameterf(sampler.handle, GL_TEXTURE_MIN_LOD, static_cast<float>(lod_min));
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SamplerInfo::SyncWithConfig(
|
||||
const Pica::TexturingRegs::TextureConfig& config) {
|
||||
|
||||
GLuint s = sampler.handle;
|
||||
|
||||
if (mag_filter != config.mag_filter) {
|
||||
mag_filter = config.mag_filter;
|
||||
glSamplerParameteri(s, GL_TEXTURE_MAG_FILTER, PicaToGL::TextureMagFilterMode(mag_filter));
|
||||
}
|
||||
|
||||
if (min_filter != config.min_filter || mip_filter != config.mip_filter) {
|
||||
min_filter = config.min_filter;
|
||||
mip_filter = config.mip_filter;
|
||||
glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER,
|
||||
PicaToGL::TextureMinFilterMode(min_filter, mip_filter));
|
||||
}
|
||||
|
||||
if (wrap_s != config.wrap_s) {
|
||||
wrap_s = config.wrap_s;
|
||||
glSamplerParameteri(s, GL_TEXTURE_WRAP_S, PicaToGL::WrapMode(wrap_s));
|
||||
}
|
||||
if (wrap_t != config.wrap_t) {
|
||||
wrap_t = config.wrap_t;
|
||||
glSamplerParameteri(s, GL_TEXTURE_WRAP_T, PicaToGL::WrapMode(wrap_t));
|
||||
}
|
||||
|
||||
if (wrap_s == TextureConfig::ClampToBorder || wrap_t == TextureConfig::ClampToBorder) {
|
||||
if (border_color != config.border_color.raw) {
|
||||
border_color = config.border_color.raw;
|
||||
auto gl_color = PicaToGL::ColorRGBA8(border_color);
|
||||
glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, gl_color.AsArray());
|
||||
}
|
||||
}
|
||||
|
||||
if (lod_min != config.lod.min_level) {
|
||||
lod_min = config.lod.min_level;
|
||||
glSamplerParameterf(s, GL_TEXTURE_MIN_LOD, static_cast<float>(lod_min));
|
||||
}
|
||||
|
||||
if (lod_max != config.lod.max_level) {
|
||||
lod_max = config.lod.max_level;
|
||||
glSamplerParameterf(s, GL_TEXTURE_MAX_LOD, static_cast<float>(lod_max));
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncClipEnabled() {
|
||||
state.clip_distance[1] = Pica::g_state.regs.rasterizer.clip_enable != 0;
|
||||
state.clip_distance[1] = regs.rasterizer.clip_enable != 0;
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncCullMode() {
|
||||
|
@ -885,7 +807,7 @@ void RasterizerOpenGL::SyncCullMode() {
|
|||
}
|
||||
|
||||
void RasterizerOpenGL::SyncBlendEnabled() {
|
||||
state.blend.enabled = (Pica::g_state.regs.framebuffer.output_merger.alphablend_enable == 1);
|
||||
state.blend.enabled = (regs.framebuffer.output_merger.alphablend_enable == 1);
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncBlendFuncs() {
|
||||
|
@ -904,8 +826,7 @@ void RasterizerOpenGL::SyncBlendFuncs() {
|
|||
}
|
||||
|
||||
void RasterizerOpenGL::SyncBlendColor() {
|
||||
auto blend_color =
|
||||
PicaToGL::ColorRGBA8(Pica::g_state.regs.framebuffer.output_merger.blend_const.raw);
|
||||
auto blend_color = PicaToGL::ColorRGBA8(regs.framebuffer.output_merger.blend_const.raw);
|
||||
state.blend.color.red = blend_color[0];
|
||||
state.blend.color.green = blend_color[1];
|
||||
state.blend.color.blue = blend_color[2];
|
||||
|
|
|
@ -55,28 +55,6 @@ private:
|
|||
void SyncFixedState() override;
|
||||
void NotifyFixedFunctionPicaRegisterChanged(u32 id) override;
|
||||
|
||||
struct SamplerInfo {
|
||||
using TextureConfig = Pica::TexturingRegs::TextureConfig;
|
||||
|
||||
OGLSampler sampler;
|
||||
|
||||
/// Creates the sampler object, initializing its state so that it's in sync with the
|
||||
/// SamplerInfo struct.
|
||||
void Create();
|
||||
/// Syncs the sampler object with the config, updating any necessary state.
|
||||
void SyncWithConfig(const TextureConfig& config);
|
||||
|
||||
private:
|
||||
TextureConfig::TextureFilter mag_filter;
|
||||
TextureConfig::TextureFilter min_filter;
|
||||
TextureConfig::TextureFilter mip_filter;
|
||||
TextureConfig::WrapMode wrap_s;
|
||||
TextureConfig::WrapMode wrap_t;
|
||||
u32 border_color;
|
||||
u32 lod_min;
|
||||
u32 lod_max;
|
||||
};
|
||||
|
||||
/// Syncs the clip enabled status to match the PICA register
|
||||
void SyncClipEnabled();
|
||||
|
||||
|
@ -156,14 +134,13 @@ private:
|
|||
OpenGLState state;
|
||||
GLuint default_texture;
|
||||
TextureRuntime runtime;
|
||||
VideoCore::RasterizerCache res_cache;
|
||||
RasterizerCache res_cache;
|
||||
std::unique_ptr<ShaderProgramManager> shader_program_manager;
|
||||
|
||||
OGLVertexArray sw_vao; // VAO for software shader draw
|
||||
OGLVertexArray hw_vao; // VAO for hardware shader / accelerate draw
|
||||
std::array<bool, 16> hw_vao_enabled_attributes{};
|
||||
|
||||
std::array<SamplerInfo, 3> texture_samplers;
|
||||
GLsizeiptr texture_buffer_size;
|
||||
OGLStreamBuffer vertex_buffer;
|
||||
OGLStreamBuffer uniform_buffer;
|
||||
|
@ -175,8 +152,6 @@ private:
|
|||
std::size_t uniform_size_aligned_vs;
|
||||
std::size_t uniform_size_aligned_fs;
|
||||
|
||||
SamplerInfo texture_cube_sampler;
|
||||
|
||||
OGLTexture texture_buffer_lut_lf;
|
||||
OGLTexture texture_buffer_lut_rg;
|
||||
OGLTexture texture_buffer_lut_rgba;
|
||||
|
|
10
src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
Normal file
10
src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
Normal file
|
@ -0,0 +1,10 @@
|
|||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "video_core/rasterizer_cache/rasterizer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_texture_runtime.h"
|
||||
|
||||
namespace VideoCore {
|
||||
template class RasterizerCache<OpenGL::Traits>;
|
||||
} // namespace VideoCore
|
|
@ -9,6 +9,7 @@
|
|||
#include "video_core/renderer_opengl/gl_driver.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/renderer_opengl/gl_texture_runtime.h"
|
||||
#include "video_core/renderer_opengl/pica_to_gl.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
|
@ -16,6 +17,7 @@ namespace {
|
|||
|
||||
using VideoCore::MapType;
|
||||
using VideoCore::PixelFormat;
|
||||
using VideoCore::SurfaceFlagBits;
|
||||
using VideoCore::SurfaceType;
|
||||
using VideoCore::TextureType;
|
||||
|
||||
|
@ -116,20 +118,11 @@ struct FramebufferInfo {
|
|||
} // Anonymous namespace
|
||||
|
||||
TextureRuntime::TextureRuntime(const Driver& driver_, VideoCore::RendererBase& renderer)
|
||||
: driver{driver_}, blit_helper{*this} {
|
||||
: driver{driver_}, blit_helper{driver} {
|
||||
for (std::size_t i = 0; i < draw_fbos.size(); ++i) {
|
||||
draw_fbos[i].Create();
|
||||
read_fbos[i].Create();
|
||||
}
|
||||
|
||||
auto add_reinterpreter = [this](PixelFormat dest,
|
||||
std::unique_ptr<FormatReinterpreterBase>&& obj) {
|
||||
const u32 dst_index = static_cast<u32>(dest);
|
||||
return reinterpreters[dst_index].push_back(std::move(obj));
|
||||
};
|
||||
|
||||
add_reinterpreter(PixelFormat::RGBA8, std::make_unique<ShaderD24S8toRGBA8>());
|
||||
add_reinterpreter(PixelFormat::RGB5A1, std::make_unique<RGBA4toRGB5A1>());
|
||||
}
|
||||
|
||||
TextureRuntime::~TextureRuntime() = default;
|
||||
|
@ -241,14 +234,30 @@ Allocation TextureRuntime::Allocate(const VideoCore::SurfaceParams& params,
|
|||
.height = params.height,
|
||||
.levels = params.levels,
|
||||
.res_scale = params.res_scale,
|
||||
.is_custom = is_custom,
|
||||
};
|
||||
}
|
||||
|
||||
bool TextureRuntime::ClearTexture(Surface& surface, const VideoCore::TextureClear& clear) {
|
||||
const auto prev_state = OpenGLState::GetCurState();
|
||||
bool TextureRuntime::Reinterpret(Surface& source, Surface& dest,
|
||||
const VideoCore::TextureBlit& blit) {
|
||||
const PixelFormat src_format = source.pixel_format;
|
||||
const PixelFormat dst_format = dest.pixel_format;
|
||||
ASSERT_MSG(src_format != dst_format, "Reinterpretation with the same format is invalid");
|
||||
if (src_format == PixelFormat::D24S8 && dst_format == PixelFormat::RGBA8) {
|
||||
blit_helper.ConvertDS24S8ToRGBA8(source, dest, blit);
|
||||
} else if (src_format == PixelFormat::RGBA4 && dst_format == PixelFormat::RGB5A1) {
|
||||
blit_helper.ConvertRGBA4ToRGB5A1(source, dest, blit);
|
||||
} else {
|
||||
LOG_WARNING(Render_OpenGL, "Unimplemented reinterpretation {} -> {}",
|
||||
VideoCore::PixelFormatAsString(src_format),
|
||||
VideoCore::PixelFormatAsString(dst_format));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Setup scissor rectangle according to the clear rectangle
|
||||
OpenGLState state;
|
||||
bool TextureRuntime::ClearTexture(Surface& surface, const VideoCore::TextureClear& clear) {
|
||||
OpenGLState state = OpenGLState::GetCurState();
|
||||
state.scissor.enabled = true;
|
||||
state.scissor.x = clear.texture_rect.left;
|
||||
state.scissor.y = clear.texture_rect.bottom;
|
||||
|
@ -257,42 +266,27 @@ bool 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);
|
||||
|
||||
switch (surface.type) {
|
||||
case SurfaceType::Color:
|
||||
case SurfaceType::Texture:
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
surface.Handle(), clear.texture_level);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
|
||||
0);
|
||||
|
||||
state.color_mask.red_enabled = true;
|
||||
state.color_mask.green_enabled = true;
|
||||
state.color_mask.blue_enabled = true;
|
||||
state.color_mask.alpha_enabled = true;
|
||||
state.Apply();
|
||||
|
||||
glClearBufferfv(GL_COLOR, 0, clear.value.color.AsArray());
|
||||
break;
|
||||
case SurfaceType::Depth:
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
|
||||
surface.Handle(), clear.texture_level);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
|
||||
|
||||
state.depth.write_mask = GL_TRUE;
|
||||
state.Apply();
|
||||
|
||||
glClearBufferfv(GL_DEPTH, 0, &clear.value.depth);
|
||||
break;
|
||||
case SurfaceType::DepthStencil:
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
|
||||
surface.Handle(), clear.texture_level);
|
||||
|
||||
state.depth.write_mask = GL_TRUE;
|
||||
state.stencil.write_mask = -1;
|
||||
state.Apply();
|
||||
|
||||
glClearBufferfi(GL_DEPTH_STENCIL, 0, clear.value.depth, clear.value.stencil);
|
||||
break;
|
||||
default:
|
||||
|
@ -300,7 +294,6 @@ bool TextureRuntime::ClearTexture(Surface& surface, const VideoCore::TextureClea
|
|||
return false;
|
||||
}
|
||||
|
||||
prev_state.Apply();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -329,13 +322,12 @@ bool TextureRuntime::BlitTextures(Surface& source, Surface& dest,
|
|||
source.Attach(GL_READ_FRAMEBUFFER, blit.src_level, blit.src_layer);
|
||||
dest.Attach(GL_DRAW_FRAMEBUFFER, blit.dst_level, blit.dst_layer);
|
||||
|
||||
// TODO (wwylele): use GL_NEAREST for shadow map texture
|
||||
// Note: shadow map is treated as RGBA8 format in PICA, as well as in the rasterizer cache, but
|
||||
// doing linear intepolation componentwise would cause incorrect value. However, for a
|
||||
// well-programmed game this code path should be rarely executed for shadow map with
|
||||
// inconsistent scale.
|
||||
// doing linear intepolation componentwise would cause incorrect value.
|
||||
const GLbitfield buffer_mask = MakeBufferMask(source.type);
|
||||
const GLenum filter = buffer_mask == GL_COLOR_BUFFER_BIT ? GL_LINEAR : GL_NEAREST;
|
||||
const bool is_shadow_map = True(source.flags & SurfaceFlagBits::ShadowMap);
|
||||
const GLenum filter =
|
||||
buffer_mask == GL_COLOR_BUFFER_BIT && !is_shadow_map ? GL_LINEAR : GL_NEAREST;
|
||||
glBlitFramebuffer(blit.src_rect.left, blit.src_rect.bottom, blit.src_rect.right,
|
||||
blit.src_rect.top, blit.dst_rect.left, blit.dst_rect.bottom,
|
||||
blit.dst_rect.right, blit.dst_rect.top, buffer_mask, filter);
|
||||
|
@ -359,11 +351,6 @@ void TextureRuntime::GenerateMipmaps(Surface& surface) {
|
|||
}
|
||||
}
|
||||
|
||||
const ReinterpreterList& TextureRuntime::GetPossibleReinterpretations(
|
||||
PixelFormat dest_format) const {
|
||||
return reinterpreters[static_cast<u32>(dest_format)];
|
||||
}
|
||||
|
||||
Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceParams& params)
|
||||
: SurfaceBase{params}, driver{&runtime_.GetDriver()}, runtime{&runtime_} {
|
||||
if (pixel_format == PixelFormat::Invalid) {
|
||||
|
@ -422,15 +409,19 @@ void Surface::UploadCustom(const VideoCore::Material* material, u32 level) {
|
|||
glActiveTexture(GL_TEXTURE0);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, Handle(0));
|
||||
if (VideoCore::IsCustomFormatCompressed(custom_format)) {
|
||||
const GLsizei image_size = static_cast<GLsizei>(color->data.size());
|
||||
glCompressedTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, width, height, tuple.format,
|
||||
image_size, color->data.data());
|
||||
} else {
|
||||
glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, width, height, tuple.format, tuple.type,
|
||||
color->data.data());
|
||||
}
|
||||
const auto upload = [&](u32 index, VideoCore::CustomTexture* texture) {
|
||||
glBindTexture(GL_TEXTURE_2D, Handle(index));
|
||||
if (VideoCore::IsCustomFormatCompressed(custom_format)) {
|
||||
const GLsizei image_size = static_cast<GLsizei>(texture->data.size());
|
||||
glCompressedTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, width, height, tuple.format,
|
||||
image_size, texture->data.data());
|
||||
} else {
|
||||
glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, width, height, tuple.format, tuple.type,
|
||||
texture->data.data());
|
||||
}
|
||||
};
|
||||
|
||||
upload(0, color);
|
||||
|
||||
const VideoCore::TextureBlit blit = {
|
||||
.src_rect = filter_rect,
|
||||
|
@ -444,15 +435,7 @@ void Surface::UploadCustom(const VideoCore::Material* material, u32 level) {
|
|||
if (!texture) {
|
||||
continue;
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, Handle(i + 1));
|
||||
if (VideoCore::IsCustomFormatCompressed(custom_format)) {
|
||||
const GLsizei image_size = static_cast<GLsizei>(texture->data.size());
|
||||
glCompressedTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, width, height, tuple.format,
|
||||
image_size, texture->data.data());
|
||||
} else {
|
||||
glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, width, height, tuple.format, tuple.type,
|
||||
texture->data.data());
|
||||
}
|
||||
upload(i + 1, texture);
|
||||
}
|
||||
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
|
@ -572,7 +555,6 @@ bool Surface::Swap(const VideoCore::Material* mat) {
|
|||
GetScaledWidth(), GetScaledHeight(), VideoCore::PixelFormatAsString(pixel_format),
|
||||
addr, width, height, VideoCore::CustomPixelFormatAsString(format));
|
||||
|
||||
is_custom = true;
|
||||
custom_format = format;
|
||||
material = mat;
|
||||
|
||||
|
@ -614,13 +596,13 @@ HostTextureTag Surface::MakeTag() const noexcept {
|
|||
.res_scale = alloc.res_scale,
|
||||
.tuple = alloc.tuple,
|
||||
.type = texture_type,
|
||||
.is_custom = is_custom,
|
||||
.is_custom = alloc.is_custom,
|
||||
.has_normal = HasNormalMap(),
|
||||
};
|
||||
}
|
||||
|
||||
Framebuffer::Framebuffer(TextureRuntime& runtime, Surface* const color, u32 color_level,
|
||||
Surface* const depth_stencil, u32 depth_level, const Pica::Regs& regs,
|
||||
Framebuffer::Framebuffer(TextureRuntime& runtime, const Surface* color, u32 color_level,
|
||||
const Surface* depth_stencil, u32 depth_level, const Pica::Regs& regs,
|
||||
Common::Rectangle<u32> surfaces_rect)
|
||||
: VideoCore::FramebufferBase{regs, color, color_level,
|
||||
depth_stencil, depth_level, surfaces_rect} {
|
||||
|
@ -692,4 +674,30 @@ Framebuffer::Framebuffer(TextureRuntime& runtime, Surface* const color, u32 colo
|
|||
|
||||
Framebuffer::~Framebuffer() = default;
|
||||
|
||||
Sampler::Sampler(TextureRuntime&, VideoCore::SamplerParams params) {
|
||||
const GLenum mag_filter = PicaToGL::TextureMagFilterMode(params.mag_filter);
|
||||
const GLenum min_filter = PicaToGL::TextureMinFilterMode(params.min_filter, params.mip_filter);
|
||||
const GLenum wrap_s = PicaToGL::WrapMode(params.wrap_s);
|
||||
const GLenum wrap_t = PicaToGL::WrapMode(params.wrap_t);
|
||||
const Common::Vec4f gl_color = PicaToGL::ColorRGBA8(params.border_color);
|
||||
const float lod_min = params.lod_min;
|
||||
const float lod_max = params.lod_max;
|
||||
|
||||
sampler.Create();
|
||||
|
||||
const GLuint handle = sampler.handle;
|
||||
glSamplerParameteri(handle, GL_TEXTURE_MAG_FILTER, mag_filter);
|
||||
glSamplerParameteri(handle, GL_TEXTURE_MIN_FILTER, min_filter);
|
||||
|
||||
glSamplerParameteri(handle, GL_TEXTURE_WRAP_S, wrap_s);
|
||||
glSamplerParameteri(handle, GL_TEXTURE_WRAP_T, wrap_t);
|
||||
|
||||
glSamplerParameterfv(handle, GL_TEXTURE_BORDER_COLOR, gl_color.AsArray());
|
||||
|
||||
glSamplerParameterf(handle, GL_TEXTURE_MIN_LOD, lod_min);
|
||||
glSamplerParameterf(handle, GL_TEXTURE_MAX_LOD, lod_max);
|
||||
}
|
||||
|
||||
Sampler::~Sampler() = default;
|
||||
|
||||
} // namespace OpenGL
|
||||
|
|
|
@ -5,9 +5,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "video_core/rasterizer_cache/framebuffer_base.h"
|
||||
#include "video_core/rasterizer_cache/surface_base.h"
|
||||
#include "video_core/rasterizer_cache/rasterizer_cache_base.h"
|
||||
#include "video_core/renderer_opengl/gl_blit_helper.h"
|
||||
#include "video_core/renderer_opengl/gl_format_reinterpreter.h"
|
||||
|
||||
namespace VideoCore {
|
||||
struct Material;
|
||||
|
@ -60,6 +59,7 @@ struct Allocation {
|
|||
u32 height;
|
||||
u32 levels;
|
||||
u32 res_scale;
|
||||
bool is_custom;
|
||||
|
||||
operator bool() const noexcept {
|
||||
return textures[0].handle;
|
||||
|
@ -76,7 +76,6 @@ class Driver;
|
|||
class TextureRuntime {
|
||||
friend class Surface;
|
||||
friend class Framebuffer;
|
||||
friend class BlitHelper;
|
||||
|
||||
public:
|
||||
explicit TextureRuntime(const Driver& driver, VideoCore::RendererBase& renderer);
|
||||
|
@ -95,12 +94,8 @@ public:
|
|||
const FormatTuple& GetFormatTuple(VideoCore::PixelFormat pixel_format) const;
|
||||
const FormatTuple& GetFormatTuple(VideoCore::CustomPixelFormat pixel_format);
|
||||
|
||||
/// Takes back ownership of the allocation for recycling
|
||||
void Recycle(const HostTextureTag tag, Allocation&& alloc);
|
||||
|
||||
/// Allocates a texture with the specified dimentions and format
|
||||
Allocation Allocate(const VideoCore::SurfaceParams& params,
|
||||
const VideoCore::Material* material = nullptr);
|
||||
/// Attempts to reinterpret a rectangle of source to another rectangle of dest
|
||||
bool Reinterpret(Surface& source, Surface& dest, const VideoCore::TextureBlit& blit);
|
||||
|
||||
/// Fills the rectangle of the texture with the clear value provided
|
||||
bool ClearTexture(Surface& surface, const VideoCore::TextureClear& clear);
|
||||
|
@ -114,10 +109,14 @@ public:
|
|||
/// Generates mipmaps for all the available levels of the texture
|
||||
void GenerateMipmaps(Surface& surface);
|
||||
|
||||
/// Returns all source formats that support reinterpretation to the dest format
|
||||
const ReinterpreterList& GetPossibleReinterpretations(VideoCore::PixelFormat dest_format) const;
|
||||
|
||||
private:
|
||||
/// Takes back ownership of the allocation for recycling
|
||||
void Recycle(const HostTextureTag tag, Allocation&& alloc);
|
||||
|
||||
/// Allocates a texture with the specified dimentions and format
|
||||
Allocation Allocate(const VideoCore::SurfaceParams& params,
|
||||
const VideoCore::Material* material = nullptr);
|
||||
|
||||
/// Returns the OpenGL driver class
|
||||
const Driver& GetDriver() const {
|
||||
return driver;
|
||||
|
@ -127,7 +126,6 @@ private:
|
|||
const Driver& driver;
|
||||
BlitHelper blit_helper;
|
||||
std::vector<u8> staging_buffer;
|
||||
std::array<ReinterpreterList, VideoCore::PIXEL_FORMAT_COUNT> reinterpreters;
|
||||
std::unordered_multimap<HostTextureTag, Allocation, HostTextureTag::Hash> alloc_cache;
|
||||
std::unordered_map<u64, OGLFramebuffer, Common::IdentityHash<u64>> framebuffer_cache;
|
||||
std::array<OGLFramebuffer, 3> draw_fbos;
|
||||
|
@ -145,24 +143,14 @@ public:
|
|||
Surface(Surface&& o) noexcept = default;
|
||||
Surface& operator=(Surface&& o) noexcept = default;
|
||||
|
||||
/// Returns the surface image handle at the provided index.
|
||||
GLuint Handle(u32 index = 1) const noexcept {
|
||||
[[nodiscard]] GLuint Handle(u32 index = 1) const noexcept {
|
||||
return alloc.handles[index];
|
||||
}
|
||||
|
||||
/// Returns the tuple of the surface allocation.
|
||||
const FormatTuple& Tuple() const noexcept {
|
||||
[[nodiscard]] const FormatTuple& Tuple() const noexcept {
|
||||
return alloc.tuple;
|
||||
}
|
||||
|
||||
/// Returns the extent of the underlying surface allocation
|
||||
VideoCore::Extent Extent() const noexcept {
|
||||
return {
|
||||
.width = alloc.width,
|
||||
.height = alloc.height,
|
||||
};
|
||||
}
|
||||
|
||||
/// Uploads pixel data in staging to a rectangle region of the surface texture
|
||||
void Upload(const VideoCore::BufferTextureCopy& upload, const VideoCore::StagingData& staging);
|
||||
|
||||
|
@ -201,8 +189,8 @@ private:
|
|||
|
||||
class Framebuffer : public VideoCore::FramebufferBase {
|
||||
public:
|
||||
explicit Framebuffer(TextureRuntime& runtime, Surface* const color, u32 color_level,
|
||||
Surface* const depth_stencil, u32 depth_level, const Pica::Regs& regs,
|
||||
explicit Framebuffer(TextureRuntime& runtime, const Surface* color, u32 color_level,
|
||||
const Surface* depth_stencil, u32 depth_level, const Pica::Regs& regs,
|
||||
Common::Rectangle<u32> surfaces_rect);
|
||||
~Framebuffer();
|
||||
|
||||
|
@ -223,4 +211,32 @@ private:
|
|||
GLuint handle{};
|
||||
};
|
||||
|
||||
class Sampler {
|
||||
public:
|
||||
explicit Sampler(TextureRuntime&, VideoCore::SamplerParams params);
|
||||
~Sampler();
|
||||
|
||||
Sampler(const Sampler&) = delete;
|
||||
Sampler& operator=(const Sampler&) = delete;
|
||||
|
||||
Sampler(Sampler&&) = default;
|
||||
Sampler& operator=(Sampler&&) = default;
|
||||
|
||||
[[nodiscard]] GLuint Handle() const noexcept {
|
||||
return sampler.handle;
|
||||
}
|
||||
|
||||
private:
|
||||
OGLSampler sampler;
|
||||
};
|
||||
|
||||
struct Traits {
|
||||
using Runtime = OpenGL::TextureRuntime;
|
||||
using Sampler = OpenGL::Sampler;
|
||||
using Surface = OpenGL::Surface;
|
||||
using Framebuffer = OpenGL::Framebuffer;
|
||||
};
|
||||
|
||||
using RasterizerCache = VideoCore::RasterizerCache<Traits>;
|
||||
|
||||
} // namespace OpenGL
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue