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:
Marshall Mohror 2020-04-07 09:12:32 -05:00 committed by GitHub
parent 3b1b8b7e1f
commit d37b0476ad
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 480 additions and 133 deletions

View file

@ -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

View 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

View 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

View file

@ -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;

View file

@ -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 {

View file

@ -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;
} }