video_core/renderer_opengl/gl_rasterizer_cache: Create Format Reinterpretation Framework (#5170)
* video_core/renderer_opengl/gl_rasterizer_cache: Create Format Reinterpretation Framework Adds RGBA4 -> RGB5A1 reinterpretation commonly used by virtual console If no matching surface can be found, ValidateSurface checks for a surface in the cache which is reinterpretable to the requested format. If that fails, the cache is checked for any surface with a matching bit-width. If one is found, the region is flushed. If not, the region is checked against dirty_regions to see if it was created entirely on the GPU. If not, then the surface is flushed. Co-Authored-By: James Rowe <jroweboy@users.noreply.github.com> Co-Authored-By: Ben <b3n30@users.noreply.github.com> temporary change to avoid merge conflicts with video dumping * re-add D24S8->RGBA8 res_scale hack * adress review comments * fix dirty region check * check for surfaces with invalid pixel format, and break logic into separate functions
This commit is contained in:
parent
3b1b8b7e1f
commit
d37b0476ad
6 changed files with 480 additions and 133 deletions
|
@ -63,6 +63,9 @@ add_library(video_core STATIC
|
||||||
renderer_opengl/texture_filters/texture_filterer.h
|
renderer_opengl/texture_filters/texture_filterer.h
|
||||||
renderer_opengl/texture_filters/xbrz/xbrz_freescale.cpp
|
renderer_opengl/texture_filters/xbrz/xbrz_freescale.cpp
|
||||||
renderer_opengl/texture_filters/xbrz/xbrz_freescale.h
|
renderer_opengl/texture_filters/xbrz/xbrz_freescale.h
|
||||||
|
#temporary, move these back in alphabetical order before merging
|
||||||
|
renderer_opengl/gl_format_reinterpreter.cpp
|
||||||
|
renderer_opengl/gl_format_reinterpreter.h
|
||||||
shader/debug_data.h
|
shader/debug_data.h
|
||||||
shader/shader.cpp
|
shader/shader.cpp
|
||||||
shader/shader.h
|
shader/shader.h
|
||||||
|
|
238
src/video_core/renderer_opengl/gl_format_reinterpreter.cpp
Normal file
238
src/video_core/renderer_opengl/gl_format_reinterpreter.cpp
Normal file
|
@ -0,0 +1,238 @@
|
||||||
|
// Copyright 2020 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/scope_exit.h"
|
||||||
|
#include "video_core/renderer_opengl/gl_format_reinterpreter.h"
|
||||||
|
#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
|
||||||
|
#include "video_core/renderer_opengl/gl_state.h"
|
||||||
|
#include "video_core/renderer_opengl/gl_vars.h"
|
||||||
|
#include "video_core/renderer_opengl/texture_filters/texture_filterer.h"
|
||||||
|
|
||||||
|
namespace OpenGL {
|
||||||
|
|
||||||
|
using PixelFormat = SurfaceParams::PixelFormat;
|
||||||
|
|
||||||
|
class RGBA4toRGB5A1 final : public FormatReinterpreterBase {
|
||||||
|
public:
|
||||||
|
RGBA4toRGB5A1() {
|
||||||
|
constexpr std::string_view vs_source = R"(
|
||||||
|
out vec2 dst_coord;
|
||||||
|
|
||||||
|
uniform mediump ivec2 dst_size;
|
||||||
|
|
||||||
|
const vec2 vertices[4] =
|
||||||
|
vec2[4](vec2(-1.0, -1.0), vec2(1.0, -1.0), vec2(-1.0, 1.0), vec2(1.0, 1.0));
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = vec4(vertices[gl_VertexID], 0.0, 1.0);
|
||||||
|
dst_coord = (vertices[gl_VertexID] / 2.0 + 0.5) * vec2(dst_size);
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
constexpr std::string_view fs_source = R"(
|
||||||
|
in mediump vec2 dst_coord;
|
||||||
|
|
||||||
|
out lowp vec4 frag_color;
|
||||||
|
|
||||||
|
uniform lowp sampler2D source;
|
||||||
|
uniform mediump ivec2 dst_size;
|
||||||
|
uniform mediump ivec2 src_size;
|
||||||
|
uniform mediump ivec2 src_offset;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
mediump ivec2 tex_coord;
|
||||||
|
if (src_size == dst_size) {
|
||||||
|
tex_coord = ivec2(dst_coord);
|
||||||
|
} else {
|
||||||
|
highp int tex_index = int(dst_coord.y) * dst_size.x + int(dst_coord.x);
|
||||||
|
mediump int y = tex_index / src_size.x;
|
||||||
|
tex_coord = ivec2(tex_index - y * src_size.x, y);
|
||||||
|
}
|
||||||
|
tex_coord -= src_offset;
|
||||||
|
|
||||||
|
lowp ivec4 rgba4 = ivec4(texelFetch(source, tex_coord, 0) * (exp2(4.0) - 1.0));
|
||||||
|
lowp ivec3 rgb5 =
|
||||||
|
((rgba4.rgb << ivec3(1, 2, 3)) | (rgba4.gba >> ivec3(3, 2, 1))) & 0x1F;
|
||||||
|
frag_color = vec4(vec3(rgb5) / (exp2(5.0) - 1.0), rgba4.a & 0x01);
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
program.Create(vs_source.data(), fs_source.data());
|
||||||
|
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 Reinterpret(GLuint src_tex, const Common::Rectangle<u32>& src_rect, GLuint read_fb_handle,
|
||||||
|
GLuint dst_tex, const Common::Rectangle<u32>& dst_rect,
|
||||||
|
GLuint draw_fb_handle) override {
|
||||||
|
OpenGLState prev_state = OpenGLState::GetCurState();
|
||||||
|
SCOPE_EXIT({ prev_state.Apply(); });
|
||||||
|
|
||||||
|
OpenGLState state;
|
||||||
|
state.texture_units[0].texture_2d = src_tex;
|
||||||
|
state.draw.draw_framebuffer = draw_fb_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, dst_tex,
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
OGLProgram program;
|
||||||
|
GLint dst_size_loc{-1}, src_size_loc{-1}, src_offset_loc{-1};
|
||||||
|
OGLVertexArray vao;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PixelBufferD24S8toABGR final : public FormatReinterpreterBase {
|
||||||
|
public:
|
||||||
|
PixelBufferD24S8toABGR() {
|
||||||
|
attributeless_vao.Create();
|
||||||
|
d24s8_abgr_buffer.Create();
|
||||||
|
d24s8_abgr_buffer_size = 0;
|
||||||
|
|
||||||
|
constexpr std::string_view vs_source = R"(
|
||||||
|
const vec2 vertices[4] = vec2[4](vec2(-1.0, -1.0), vec2(1.0, -1.0),
|
||||||
|
vec2(-1.0, 1.0), vec2(1.0, 1.0));
|
||||||
|
void main() {
|
||||||
|
gl_Position = vec4(vertices[gl_VertexID], 0.0, 1.0);
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
std::string fs_source = GLES ? fragment_shader_precision_OES : "";
|
||||||
|
fs_source += R"(
|
||||||
|
uniform samplerBuffer tbo;
|
||||||
|
uniform vec2 tbo_size;
|
||||||
|
uniform vec4 viewport;
|
||||||
|
|
||||||
|
out vec4 color;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 tbo_coord = (gl_FragCoord.xy - viewport.xy) * tbo_size / viewport.zw;
|
||||||
|
int tbo_offset = int(tbo_coord.y) * int(tbo_size.x) + int(tbo_coord.x);
|
||||||
|
color = texelFetch(tbo, tbo_offset).rabg;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
d24s8_abgr_shader.Create(vs_source.data(), fs_source.c_str());
|
||||||
|
|
||||||
|
OpenGLState state = OpenGLState::GetCurState();
|
||||||
|
GLuint old_program = state.draw.shader_program;
|
||||||
|
state.draw.shader_program = d24s8_abgr_shader.handle;
|
||||||
|
state.Apply();
|
||||||
|
|
||||||
|
GLint tbo_u_id = glGetUniformLocation(d24s8_abgr_shader.handle, "tbo");
|
||||||
|
ASSERT(tbo_u_id != -1);
|
||||||
|
glUniform1i(tbo_u_id, 0);
|
||||||
|
|
||||||
|
state.draw.shader_program = old_program;
|
||||||
|
state.Apply();
|
||||||
|
|
||||||
|
d24s8_abgr_tbo_size_u_id = glGetUniformLocation(d24s8_abgr_shader.handle, "tbo_size");
|
||||||
|
ASSERT(d24s8_abgr_tbo_size_u_id != -1);
|
||||||
|
d24s8_abgr_viewport_u_id = glGetUniformLocation(d24s8_abgr_shader.handle, "viewport");
|
||||||
|
ASSERT(d24s8_abgr_viewport_u_id != -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
~PixelBufferD24S8toABGR() {}
|
||||||
|
|
||||||
|
void Reinterpret(GLuint src_tex, const Common::Rectangle<u32>& src_rect, GLuint read_fb_handle,
|
||||||
|
GLuint dst_tex, const Common::Rectangle<u32>& dst_rect,
|
||||||
|
GLuint draw_fb_handle) override {
|
||||||
|
OpenGLState prev_state = OpenGLState::GetCurState();
|
||||||
|
SCOPE_EXIT({ prev_state.Apply(); });
|
||||||
|
|
||||||
|
OpenGLState state;
|
||||||
|
state.draw.read_framebuffer = read_fb_handle;
|
||||||
|
state.draw.draw_framebuffer = draw_fb_handle;
|
||||||
|
state.Apply();
|
||||||
|
|
||||||
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, d24s8_abgr_buffer.handle);
|
||||||
|
|
||||||
|
GLsizeiptr target_pbo_size =
|
||||||
|
static_cast<GLsizeiptr>(src_rect.GetWidth()) * src_rect.GetHeight() * 4;
|
||||||
|
if (target_pbo_size > d24s8_abgr_buffer_size) {
|
||||||
|
d24s8_abgr_buffer_size = target_pbo_size * 2;
|
||||||
|
glBufferData(GL_PIXEL_PACK_BUFFER, d24s8_abgr_buffer_size, nullptr, GL_STREAM_COPY);
|
||||||
|
}
|
||||||
|
|
||||||
|
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||||
|
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
|
||||||
|
src_tex, 0);
|
||||||
|
glReadPixels(static_cast<GLint>(src_rect.left), static_cast<GLint>(src_rect.bottom),
|
||||||
|
static_cast<GLsizei>(src_rect.GetWidth()),
|
||||||
|
static_cast<GLsizei>(src_rect.GetHeight()), GL_DEPTH_STENCIL,
|
||||||
|
GL_UNSIGNED_INT_24_8, 0);
|
||||||
|
|
||||||
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||||
|
|
||||||
|
// PBO now contains src_tex in RABG format
|
||||||
|
state.draw.shader_program = d24s8_abgr_shader.handle;
|
||||||
|
state.draw.vertex_array = attributeless_vao.handle;
|
||||||
|
state.viewport.x = static_cast<GLint>(dst_rect.left);
|
||||||
|
state.viewport.y = static_cast<GLint>(dst_rect.bottom);
|
||||||
|
state.viewport.width = static_cast<GLsizei>(dst_rect.GetWidth());
|
||||||
|
state.viewport.height = static_cast<GLsizei>(dst_rect.GetHeight());
|
||||||
|
state.Apply();
|
||||||
|
|
||||||
|
OGLTexture tbo;
|
||||||
|
tbo.Create();
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
glBindTexture(GL_TEXTURE_BUFFER, tbo.handle);
|
||||||
|
glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA8, d24s8_abgr_buffer.handle);
|
||||||
|
|
||||||
|
glUniform2f(d24s8_abgr_tbo_size_u_id, static_cast<GLfloat>(src_rect.GetWidth()),
|
||||||
|
static_cast<GLfloat>(src_rect.GetHeight()));
|
||||||
|
glUniform4f(d24s8_abgr_viewport_u_id, static_cast<GLfloat>(state.viewport.x),
|
||||||
|
static_cast<GLfloat>(state.viewport.y),
|
||||||
|
static_cast<GLfloat>(state.viewport.width),
|
||||||
|
static_cast<GLfloat>(state.viewport.height));
|
||||||
|
|
||||||
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex,
|
||||||
|
0);
|
||||||
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
|
||||||
|
0);
|
||||||
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_BUFFER, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
OGLVertexArray attributeless_vao;
|
||||||
|
OGLBuffer d24s8_abgr_buffer;
|
||||||
|
GLsizeiptr d24s8_abgr_buffer_size;
|
||||||
|
OGLProgram d24s8_abgr_shader;
|
||||||
|
GLint d24s8_abgr_tbo_size_u_id;
|
||||||
|
GLint d24s8_abgr_viewport_u_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
FormatReinterpreterOpenGL::FormatReinterpreterOpenGL() {
|
||||||
|
reinterpreters.emplace(PixelFormatPair{PixelFormat::RGBA8, PixelFormat::D24S8},
|
||||||
|
std::make_unique<PixelBufferD24S8toABGR>());
|
||||||
|
reinterpreters.emplace(PixelFormatPair{PixelFormat::RGB5A1, PixelFormat::RGBA4},
|
||||||
|
std::make_unique<RGBA4toRGB5A1>());
|
||||||
|
}
|
||||||
|
|
||||||
|
FormatReinterpreterOpenGL::~FormatReinterpreterOpenGL() = default;
|
||||||
|
|
||||||
|
std::pair<FormatReinterpreterOpenGL::ReinterpreterMap::iterator,
|
||||||
|
FormatReinterpreterOpenGL::ReinterpreterMap::iterator>
|
||||||
|
FormatReinterpreterOpenGL::GetPossibleReinterpretations(PixelFormat dst_format) {
|
||||||
|
return reinterpreters.equal_range(dst_format);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace OpenGL
|
62
src/video_core/renderer_opengl/gl_format_reinterpreter.h
Normal file
62
src/video_core/renderer_opengl/gl_format_reinterpreter.h
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
// Copyright 2020 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <glad/glad.h>
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "common/math_util.h"
|
||||||
|
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||||
|
#include "video_core/renderer_opengl/gl_surface_params.h"
|
||||||
|
|
||||||
|
namespace OpenGL {
|
||||||
|
|
||||||
|
class RasterizerCacheOpenGL;
|
||||||
|
|
||||||
|
struct PixelFormatPair {
|
||||||
|
const SurfaceParams::PixelFormat dst_format, src_format;
|
||||||
|
struct less {
|
||||||
|
using is_transparent = void;
|
||||||
|
constexpr bool operator()(OpenGL::PixelFormatPair lhs, OpenGL::PixelFormatPair rhs) const {
|
||||||
|
return std::tie(lhs.dst_format, lhs.src_format) <
|
||||||
|
std::tie(rhs.dst_format, rhs.src_format);
|
||||||
|
}
|
||||||
|
constexpr bool operator()(OpenGL::SurfaceParams::PixelFormat lhs,
|
||||||
|
OpenGL::PixelFormatPair rhs) const {
|
||||||
|
return lhs < rhs.dst_format;
|
||||||
|
}
|
||||||
|
constexpr bool operator()(OpenGL::PixelFormatPair lhs,
|
||||||
|
OpenGL::SurfaceParams::PixelFormat rhs) const {
|
||||||
|
return lhs.dst_format < rhs;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
class FormatReinterpreterBase {
|
||||||
|
public:
|
||||||
|
virtual ~FormatReinterpreterBase() = default;
|
||||||
|
|
||||||
|
virtual void Reinterpret(GLuint src_tex, const Common::Rectangle<u32>& src_rect,
|
||||||
|
GLuint read_fb_handle, GLuint dst_tex,
|
||||||
|
const Common::Rectangle<u32>& dst_rect, GLuint draw_fb_handle) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FormatReinterpreterOpenGL : NonCopyable {
|
||||||
|
using ReinterpreterMap =
|
||||||
|
std::map<PixelFormatPair, std::unique_ptr<FormatReinterpreterBase>, PixelFormatPair::less>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit FormatReinterpreterOpenGL();
|
||||||
|
~FormatReinterpreterOpenGL();
|
||||||
|
|
||||||
|
std::pair<ReinterpreterMap::iterator, ReinterpreterMap::iterator> GetPossibleReinterpretations(
|
||||||
|
SurfaceParams::PixelFormat dst_format);
|
||||||
|
|
||||||
|
private:
|
||||||
|
ReinterpreterMap reinterpreters;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace OpenGL
|
|
@ -32,6 +32,7 @@
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
#include "video_core/pica_state.h"
|
#include "video_core/pica_state.h"
|
||||||
#include "video_core/renderer_base.h"
|
#include "video_core/renderer_base.h"
|
||||||
|
#include "video_core/renderer_opengl/gl_format_reinterpreter.h"
|
||||||
#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
|
#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
|
||||||
#include "video_core/renderer_opengl/gl_state.h"
|
#include "video_core/renderer_opengl/gl_state.h"
|
||||||
#include "video_core/renderer_opengl/gl_vars.h"
|
#include "video_core/renderer_opengl/gl_vars.h"
|
||||||
|
@ -1063,54 +1064,10 @@ RasterizerCacheOpenGL::RasterizerCacheOpenGL() {
|
||||||
resolution_scale_factor = VideoCore::GetResolutionScaleFactor();
|
resolution_scale_factor = VideoCore::GetResolutionScaleFactor();
|
||||||
texture_filterer = std::make_unique<TextureFilterer>(Settings::values.texture_filter_name,
|
texture_filterer = std::make_unique<TextureFilterer>(Settings::values.texture_filter_name,
|
||||||
resolution_scale_factor);
|
resolution_scale_factor);
|
||||||
|
format_reinterpreter = std::make_unique<FormatReinterpreterOpenGL>();
|
||||||
|
|
||||||
read_framebuffer.Create();
|
read_framebuffer.Create();
|
||||||
draw_framebuffer.Create();
|
draw_framebuffer.Create();
|
||||||
|
|
||||||
attributeless_vao.Create();
|
|
||||||
|
|
||||||
d24s8_abgr_buffer.Create();
|
|
||||||
d24s8_abgr_buffer_size = 0;
|
|
||||||
|
|
||||||
std::string vs_source = R"(
|
|
||||||
const vec2 vertices[4] = vec2[4](vec2(-1.0, -1.0), vec2(1.0, -1.0), vec2(-1.0, 1.0), vec2(1.0, 1.0));
|
|
||||||
void main() {
|
|
||||||
gl_Position = vec4(vertices[gl_VertexID], 0.0, 1.0);
|
|
||||||
}
|
|
||||||
)";
|
|
||||||
|
|
||||||
std::string fs_source = GLES ? fragment_shader_precision_OES : "";
|
|
||||||
fs_source += R"(
|
|
||||||
uniform samplerBuffer tbo;
|
|
||||||
uniform vec2 tbo_size;
|
|
||||||
uniform vec4 viewport;
|
|
||||||
|
|
||||||
out vec4 color;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec2 tbo_coord = (gl_FragCoord.xy - viewport.xy) * tbo_size / viewport.zw;
|
|
||||||
int tbo_offset = int(tbo_coord.y) * int(tbo_size.x) + int(tbo_coord.x);
|
|
||||||
color = texelFetch(tbo, tbo_offset).rabg;
|
|
||||||
}
|
|
||||||
)";
|
|
||||||
d24s8_abgr_shader.Create(vs_source.c_str(), fs_source.c_str());
|
|
||||||
|
|
||||||
OpenGLState state = OpenGLState::GetCurState();
|
|
||||||
GLuint old_program = state.draw.shader_program;
|
|
||||||
state.draw.shader_program = d24s8_abgr_shader.handle;
|
|
||||||
state.Apply();
|
|
||||||
|
|
||||||
GLint tbo_u_id = glGetUniformLocation(d24s8_abgr_shader.handle, "tbo");
|
|
||||||
ASSERT(tbo_u_id != -1);
|
|
||||||
glUniform1i(tbo_u_id, 0);
|
|
||||||
|
|
||||||
state.draw.shader_program = old_program;
|
|
||||||
state.Apply();
|
|
||||||
|
|
||||||
d24s8_abgr_tbo_size_u_id = glGetUniformLocation(d24s8_abgr_shader.handle, "tbo_size");
|
|
||||||
ASSERT(d24s8_abgr_tbo_size_u_id != -1);
|
|
||||||
d24s8_abgr_viewport_u_id = glGetUniformLocation(d24s8_abgr_shader.handle, "viewport");
|
|
||||||
ASSERT(d24s8_abgr_viewport_u_id != -1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RasterizerCacheOpenGL::~RasterizerCacheOpenGL() {
|
RasterizerCacheOpenGL::~RasterizerCacheOpenGL() {
|
||||||
|
@ -1136,64 +1093,6 @@ bool RasterizerCacheOpenGL::BlitSurfaces(const Surface& src_surface,
|
||||||
draw_framebuffer.handle);
|
draw_framebuffer.handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerCacheOpenGL::ConvertD24S8toABGR(GLuint src_tex,
|
|
||||||
const Common::Rectangle<u32>& src_rect,
|
|
||||||
GLuint dst_tex,
|
|
||||||
const Common::Rectangle<u32>& dst_rect) {
|
|
||||||
OpenGLState prev_state = OpenGLState::GetCurState();
|
|
||||||
SCOPE_EXIT({ prev_state.Apply(); });
|
|
||||||
|
|
||||||
OpenGLState state;
|
|
||||||
state.draw.read_framebuffer = read_framebuffer.handle;
|
|
||||||
state.draw.draw_framebuffer = draw_framebuffer.handle;
|
|
||||||
state.Apply();
|
|
||||||
|
|
||||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, d24s8_abgr_buffer.handle);
|
|
||||||
|
|
||||||
GLsizeiptr target_pbo_size = src_rect.GetWidth() * src_rect.GetHeight() * 4;
|
|
||||||
if (target_pbo_size > d24s8_abgr_buffer_size) {
|
|
||||||
d24s8_abgr_buffer_size = target_pbo_size * 2;
|
|
||||||
glBufferData(GL_PIXEL_PACK_BUFFER, d24s8_abgr_buffer_size, nullptr, GL_STREAM_COPY);
|
|
||||||
}
|
|
||||||
|
|
||||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
|
||||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, src_tex,
|
|
||||||
0);
|
|
||||||
glReadPixels(static_cast<GLint>(src_rect.left), static_cast<GLint>(src_rect.bottom),
|
|
||||||
static_cast<GLsizei>(src_rect.GetWidth()),
|
|
||||||
static_cast<GLsizei>(src_rect.GetHeight()), GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8,
|
|
||||||
0);
|
|
||||||
|
|
||||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
|
||||||
|
|
||||||
// PBO now contains src_tex in RABG format
|
|
||||||
state.draw.shader_program = d24s8_abgr_shader.handle;
|
|
||||||
state.draw.vertex_array = attributeless_vao.handle;
|
|
||||||
state.viewport.x = static_cast<GLint>(dst_rect.left);
|
|
||||||
state.viewport.y = static_cast<GLint>(dst_rect.bottom);
|
|
||||||
state.viewport.width = static_cast<GLsizei>(dst_rect.GetWidth());
|
|
||||||
state.viewport.height = static_cast<GLsizei>(dst_rect.GetHeight());
|
|
||||||
state.Apply();
|
|
||||||
|
|
||||||
OGLTexture tbo;
|
|
||||||
tbo.Create();
|
|
||||||
glActiveTexture(GL_TEXTURE0);
|
|
||||||
glBindTexture(GL_TEXTURE_BUFFER, tbo.handle);
|
|
||||||
glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA8, d24s8_abgr_buffer.handle);
|
|
||||||
|
|
||||||
glUniform2f(d24s8_abgr_tbo_size_u_id, static_cast<GLfloat>(src_rect.GetWidth()),
|
|
||||||
static_cast<GLfloat>(src_rect.GetHeight()));
|
|
||||||
glUniform4f(d24s8_abgr_viewport_u_id, static_cast<GLfloat>(state.viewport.x),
|
|
||||||
static_cast<GLfloat>(state.viewport.y), static_cast<GLfloat>(state.viewport.width),
|
|
||||||
static_cast<GLfloat>(state.viewport.height));
|
|
||||||
|
|
||||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex, 0);
|
|
||||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
|
|
||||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_BUFFER, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, ScaleMatch match_res_scale,
|
Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, ScaleMatch match_res_scale,
|
||||||
bool load_if_create) {
|
bool load_if_create) {
|
||||||
if (params.addr == 0 || params.height * params.width == 0) {
|
if (params.addr == 0 || params.height * params.width == 0) {
|
||||||
|
@ -1721,9 +1620,15 @@ void RasterizerCacheOpenGL::ValidateSurface(const Surface& surface, PAddr addr,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto validate_regions = surface->invalid_regions & validate_interval;
|
||||||
|
auto notify_validated = [&](SurfaceInterval interval) {
|
||||||
|
surface->invalid_regions.erase(interval);
|
||||||
|
validate_regions.erase(interval);
|
||||||
|
};
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const auto it = surface->invalid_regions.find(validate_interval);
|
const auto it = validate_regions.begin();
|
||||||
if (it == surface->invalid_regions.end())
|
if (it == validate_regions.end())
|
||||||
break;
|
break;
|
||||||
|
|
||||||
const auto interval = *it & validate_interval;
|
const auto interval = *it & validate_interval;
|
||||||
|
@ -1735,27 +1640,27 @@ void RasterizerCacheOpenGL::ValidateSurface(const Surface& surface, PAddr addr,
|
||||||
if (copy_surface != nullptr) {
|
if (copy_surface != nullptr) {
|
||||||
SurfaceInterval copy_interval = params.GetCopyableInterval(copy_surface);
|
SurfaceInterval copy_interval = params.GetCopyableInterval(copy_surface);
|
||||||
CopySurface(copy_surface, surface, copy_interval);
|
CopySurface(copy_surface, surface, copy_interval);
|
||||||
surface->invalid_regions.erase(copy_interval);
|
notify_validated(copy_interval);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// D24S8 to RGBA8
|
// Try to find surface in cache with different format
|
||||||
if (surface->pixel_format == PixelFormat::RGBA8) {
|
// that can can be reinterpreted to the requested format.
|
||||||
params.pixel_format = PixelFormat::D24S8;
|
if (ValidateByReinterpretation(surface, params, interval)) {
|
||||||
Surface reinterpret_surface =
|
notify_validated(interval);
|
||||||
FindMatch<MatchFlags::Copy>(surface_cache, params, ScaleMatch::Ignore, interval);
|
continue;
|
||||||
if (reinterpret_surface != nullptr) {
|
}
|
||||||
ASSERT(reinterpret_surface->pixel_format == PixelFormat::D24S8);
|
// Could not find a matching reinterpreter, check if we need to implement a
|
||||||
|
// reinterpreter
|
||||||
SurfaceInterval convert_interval = params.GetCopyableInterval(reinterpret_surface);
|
if (NoUnimplementedReinterpretations(surface, params, interval) &&
|
||||||
SurfaceParams convert_params = surface->FromInterval(convert_interval);
|
!IntervalHasInvalidPixelFormat(params, interval)) {
|
||||||
auto src_rect = reinterpret_surface->GetScaledSubRect(convert_params);
|
// No surfaces were found in the cache that had a matching bit-width.
|
||||||
auto dest_rect = surface->GetScaledSubRect(convert_params);
|
// If the region was created entirely on the GPU,
|
||||||
|
// assume it was a developer mistake and skip flushing.
|
||||||
ConvertD24S8toABGR(reinterpret_surface->texture.handle, src_rect,
|
if (boost::icl::contains(dirty_regions, interval)) {
|
||||||
surface->texture.handle, dest_rect);
|
LOG_DEBUG(Render_OpenGL, "Region created fully on GPU and reinterpretation is "
|
||||||
|
"invalid. Skipping validation");
|
||||||
surface->invalid_regions.erase(convert_interval);
|
validate_regions.erase(interval);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1765,10 +1670,103 @@ void RasterizerCacheOpenGL::ValidateSurface(const Surface& surface, PAddr addr,
|
||||||
surface->LoadGLBuffer(params.addr, params.end);
|
surface->LoadGLBuffer(params.addr, params.end);
|
||||||
surface->UploadGLTexture(surface->GetSubRect(params), read_framebuffer.handle,
|
surface->UploadGLTexture(surface->GetSubRect(params), read_framebuffer.handle,
|
||||||
draw_framebuffer.handle);
|
draw_framebuffer.handle);
|
||||||
surface->invalid_regions.erase(params.GetInterval());
|
notify_validated(params.GetInterval());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RasterizerCacheOpenGL::NoUnimplementedReinterpretations(const Surface& surface,
|
||||||
|
SurfaceParams& params,
|
||||||
|
const SurfaceInterval& interval) {
|
||||||
|
static constexpr std::array<PixelFormat, 17> all_formats{
|
||||||
|
PixelFormat::RGBA8, PixelFormat::RGB8, PixelFormat::RGB5A1, PixelFormat::RGB565,
|
||||||
|
PixelFormat::RGBA4, PixelFormat::IA8, PixelFormat::RG8, PixelFormat::I8,
|
||||||
|
PixelFormat::A8, PixelFormat::IA4, PixelFormat::I4, PixelFormat::A4,
|
||||||
|
PixelFormat::ETC1, PixelFormat::ETC1A4, PixelFormat::D16, PixelFormat::D24,
|
||||||
|
PixelFormat::D24S8,
|
||||||
|
};
|
||||||
|
bool implemented = true;
|
||||||
|
for (PixelFormat format : all_formats) {
|
||||||
|
if (SurfaceParams::GetFormatBpp(format) == surface->GetFormatBpp()) {
|
||||||
|
params.pixel_format = format;
|
||||||
|
// This could potentially be expensive,
|
||||||
|
// although experimentally it hasn't been too bad
|
||||||
|
Surface test_surface =
|
||||||
|
FindMatch<MatchFlags::Copy>(surface_cache, params, ScaleMatch::Ignore, interval);
|
||||||
|
if (test_surface != nullptr) {
|
||||||
|
LOG_WARNING(Render_OpenGL, "Missing pixel_format reinterpreter: {} -> {}",
|
||||||
|
SurfaceParams::PixelFormatAsString(format),
|
||||||
|
SurfaceParams::PixelFormatAsString(surface->pixel_format));
|
||||||
|
implemented = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return implemented;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RasterizerCacheOpenGL::IntervalHasInvalidPixelFormat(SurfaceParams& params,
|
||||||
|
const SurfaceInterval& interval) {
|
||||||
|
params.pixel_format = PixelFormat::Invalid;
|
||||||
|
for (const auto& set : RangeFromInterval(surface_cache, interval))
|
||||||
|
for (const auto& surface : set.second)
|
||||||
|
if (surface->pixel_format == PixelFormat::Invalid) {
|
||||||
|
LOG_WARNING(Render_OpenGL, "Surface found with invalid pixel format");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RasterizerCacheOpenGL::ValidateByReinterpretation(const Surface& surface,
|
||||||
|
SurfaceParams& params,
|
||||||
|
const SurfaceInterval& interval) {
|
||||||
|
auto [cvt_begin, cvt_end] =
|
||||||
|
format_reinterpreter->GetPossibleReinterpretations(surface->pixel_format);
|
||||||
|
for (auto reinterpreter = cvt_begin; reinterpreter != cvt_end; ++reinterpreter) {
|
||||||
|
PixelFormat format = reinterpreter->first.src_format;
|
||||||
|
params.pixel_format = format;
|
||||||
|
Surface reinterpret_surface =
|
||||||
|
FindMatch<MatchFlags::Copy>(surface_cache, params, ScaleMatch::Ignore, interval);
|
||||||
|
|
||||||
|
if (reinterpret_surface != nullptr) {
|
||||||
|
SurfaceInterval reinterpret_interval = params.GetCopyableInterval(reinterpret_surface);
|
||||||
|
SurfaceParams reinterpret_params = surface->FromInterval(reinterpret_interval);
|
||||||
|
auto src_rect = reinterpret_surface->GetScaledSubRect(reinterpret_params);
|
||||||
|
auto dest_rect = surface->GetScaledSubRect(reinterpret_params);
|
||||||
|
|
||||||
|
if (!texture_filterer->IsNull() && reinterpret_surface->res_scale == 1 &&
|
||||||
|
surface->res_scale == resolution_scale_factor) {
|
||||||
|
// The destination surface is either a framebuffer, or a filtered texture.
|
||||||
|
OGLTexture tmp_tex;
|
||||||
|
tmp_tex.Create();
|
||||||
|
// Create an intermediate surface to convert to before blitting to the
|
||||||
|
// destination.
|
||||||
|
Common::Rectangle<u32> tmp_rect{0, dest_rect.GetHeight() / resolution_scale_factor,
|
||||||
|
dest_rect.GetWidth() / resolution_scale_factor, 0};
|
||||||
|
AllocateSurfaceTexture(tmp_tex.handle,
|
||||||
|
GetFormatTuple(reinterpreter->first.dst_format),
|
||||||
|
tmp_rect.right, tmp_rect.top);
|
||||||
|
reinterpreter->second->Reinterpret(reinterpret_surface->texture.handle, src_rect,
|
||||||
|
read_framebuffer.handle, tmp_tex.handle,
|
||||||
|
tmp_rect, draw_framebuffer.handle);
|
||||||
|
SurfaceParams::SurfaceType type =
|
||||||
|
SurfaceParams::GetFormatType(reinterpreter->first.dst_format);
|
||||||
|
|
||||||
|
if (!texture_filterer->Filter(tmp_tex.handle, tmp_rect, surface->texture.handle,
|
||||||
|
dest_rect, type, read_framebuffer.handle,
|
||||||
|
draw_framebuffer.handle)) {
|
||||||
|
BlitTextures(tmp_tex.handle, tmp_rect, surface->texture.handle, dest_rect, type,
|
||||||
|
read_framebuffer.handle, draw_framebuffer.handle);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reinterpreter->second->Reinterpret(reinterpret_surface->texture.handle, src_rect,
|
||||||
|
read_framebuffer.handle, surface->texture.handle,
|
||||||
|
dest_rect, draw_framebuffer.handle);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void RasterizerCacheOpenGL::FlushRegion(PAddr addr, u32 size, Surface flush_surface) {
|
void RasterizerCacheOpenGL::FlushRegion(PAddr addr, u32 size, Surface flush_surface) {
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -34,6 +34,7 @@ namespace OpenGL {
|
||||||
|
|
||||||
class RasterizerCacheOpenGL;
|
class RasterizerCacheOpenGL;
|
||||||
class TextureFilterer;
|
class TextureFilterer;
|
||||||
|
class FormatReinterpreterOpenGL;
|
||||||
|
|
||||||
struct TextureCubeConfig {
|
struct TextureCubeConfig {
|
||||||
PAddr px;
|
PAddr px;
|
||||||
|
@ -240,9 +241,6 @@ public:
|
||||||
bool BlitSurfaces(const Surface& src_surface, const Common::Rectangle<u32>& src_rect,
|
bool BlitSurfaces(const Surface& src_surface, const Common::Rectangle<u32>& src_rect,
|
||||||
const Surface& dst_surface, const Common::Rectangle<u32>& dst_rect);
|
const Surface& dst_surface, const Common::Rectangle<u32>& dst_rect);
|
||||||
|
|
||||||
void ConvertD24S8toABGR(GLuint src_tex, const Common::Rectangle<u32>& src_rect, GLuint dst_tex,
|
|
||||||
const Common::Rectangle<u32>& dst_rect);
|
|
||||||
|
|
||||||
/// Copy one surface's region to another
|
/// Copy one surface's region to another
|
||||||
void CopySurface(const Surface& src_surface, const Surface& dst_surface,
|
void CopySurface(const Surface& src_surface, const Surface& dst_surface,
|
||||||
SurfaceInterval copy_interval);
|
SurfaceInterval copy_interval);
|
||||||
|
@ -288,6 +286,18 @@ private:
|
||||||
/// Update surface's texture for given region when necessary
|
/// Update surface's texture for given region when necessary
|
||||||
void ValidateSurface(const Surface& surface, PAddr addr, u32 size);
|
void ValidateSurface(const Surface& surface, PAddr addr, u32 size);
|
||||||
|
|
||||||
|
// Returns false if there is a surface in the cache at the interval with the same bit-width,
|
||||||
|
bool NoUnimplementedReinterpretations(const OpenGL::Surface& surface,
|
||||||
|
OpenGL::SurfaceParams& params,
|
||||||
|
const OpenGL::SurfaceInterval& interval);
|
||||||
|
|
||||||
|
// Return true if a surface with an invalid pixel format exists at the interval
|
||||||
|
bool IntervalHasInvalidPixelFormat(SurfaceParams& params, const SurfaceInterval& interval);
|
||||||
|
|
||||||
|
// Attempt to find a reinterpretable surface in the cache and use it to copy for validation
|
||||||
|
bool ValidateByReinterpretation(const Surface& surface, SurfaceParams& params,
|
||||||
|
const SurfaceInterval& interval);
|
||||||
|
|
||||||
/// Create a new surface
|
/// Create a new surface
|
||||||
Surface CreateSurface(const SurfaceParams& params);
|
Surface CreateSurface(const SurfaceParams& params);
|
||||||
|
|
||||||
|
@ -308,18 +318,13 @@ private:
|
||||||
OGLFramebuffer read_framebuffer;
|
OGLFramebuffer read_framebuffer;
|
||||||
OGLFramebuffer draw_framebuffer;
|
OGLFramebuffer draw_framebuffer;
|
||||||
|
|
||||||
OGLVertexArray attributeless_vao;
|
|
||||||
OGLBuffer d24s8_abgr_buffer;
|
|
||||||
GLsizeiptr d24s8_abgr_buffer_size;
|
|
||||||
OGLProgram d24s8_abgr_shader;
|
|
||||||
GLint d24s8_abgr_tbo_size_u_id;
|
|
||||||
GLint d24s8_abgr_viewport_u_id;
|
|
||||||
u16 resolution_scale_factor;
|
u16 resolution_scale_factor;
|
||||||
|
|
||||||
std::unordered_map<TextureCubeConfig, CachedTextureCube> texture_cube_cache;
|
std::unordered_map<TextureCubeConfig, CachedTextureCube> texture_cube_cache;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
std::unique_ptr<TextureFilterer> texture_filterer;
|
std::unique_ptr<TextureFilterer> texture_filterer;
|
||||||
|
std::unique_ptr<FormatReinterpreterOpenGL> format_reinterpreter;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FormatTuple {
|
struct FormatTuple {
|
||||||
|
|
|
@ -91,6 +91,47 @@ public:
|
||||||
return GetFormatBpp(pixel_format);
|
return GetFormatBpp(pixel_format);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::string_view PixelFormatAsString(PixelFormat format) {
|
||||||
|
switch (format) {
|
||||||
|
case PixelFormat::RGBA8:
|
||||||
|
return "RGBA8";
|
||||||
|
case PixelFormat::RGB8:
|
||||||
|
return "RGB8";
|
||||||
|
case PixelFormat::RGB5A1:
|
||||||
|
return "RGB5A1";
|
||||||
|
case PixelFormat::RGB565:
|
||||||
|
return "RGB565";
|
||||||
|
case PixelFormat::RGBA4:
|
||||||
|
return "RGBA4";
|
||||||
|
case PixelFormat::IA8:
|
||||||
|
return "IA8";
|
||||||
|
case PixelFormat::RG8:
|
||||||
|
return "RG8";
|
||||||
|
case PixelFormat::I8:
|
||||||
|
return "I8";
|
||||||
|
case PixelFormat::A8:
|
||||||
|
return "A8";
|
||||||
|
case PixelFormat::IA4:
|
||||||
|
return "IA4";
|
||||||
|
case PixelFormat::I4:
|
||||||
|
return "I4";
|
||||||
|
case PixelFormat::A4:
|
||||||
|
return "A4";
|
||||||
|
case PixelFormat::ETC1:
|
||||||
|
return "ETC1";
|
||||||
|
case PixelFormat::ETC1A4:
|
||||||
|
return "ETC1A4";
|
||||||
|
case PixelFormat::D16:
|
||||||
|
return "D16";
|
||||||
|
case PixelFormat::D24:
|
||||||
|
return "D24";
|
||||||
|
case PixelFormat::D24S8:
|
||||||
|
return "D24S8";
|
||||||
|
default:
|
||||||
|
return "Not a real pixel format";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static PixelFormat PixelFormatFromTextureFormat(Pica::TexturingRegs::TextureFormat format) {
|
static PixelFormat PixelFormatFromTextureFormat(Pica::TexturingRegs::TextureFormat format) {
|
||||||
return ((unsigned int)format < 14) ? (PixelFormat)format : PixelFormat::Invalid;
|
return ((unsigned int)format < 14) ? (PixelFormat)format : PixelFormat::Invalid;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue