Merge pull request #3281 from jroweboy/texcache-pt2
Texture Cache Rework
This commit is contained in:
commit
4befbddc34
24 changed files with 2038 additions and 1107 deletions
|
@ -38,6 +38,9 @@ public:
|
|||
/// Notify rasterizer that any caches of the specified region should be flushed to 3DS memory
|
||||
virtual void FlushRegion(PAddr addr, u32 size) = 0;
|
||||
|
||||
/// Notify rasterizer that any caches of the specified region should be invalidated
|
||||
virtual void InvalidateRegion(PAddr addr, u32 size) = 0;
|
||||
|
||||
/// Notify rasterizer that any caches of the specified region should be flushed to 3DS memory
|
||||
/// and invalidated
|
||||
virtual void FlushAndInvalidateRegion(PAddr addr, u32 size) = 0;
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <glad/glad.h>
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/color.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/math_util.h"
|
||||
#include "common/microprofile.h"
|
||||
|
@ -23,6 +23,9 @@
|
|||
#include "video_core/renderer_opengl/pica_to_gl.h"
|
||||
#include "video_core/renderer_opengl/renderer_opengl.h"
|
||||
|
||||
using PixelFormat = SurfaceParams::PixelFormat;
|
||||
using SurfaceType = SurfaceParams::SurfaceType;
|
||||
|
||||
MICROPROFILE_DEFINE(OpenGL_Drawing, "OpenGL", "Drawing", MP_RGB(128, 128, 192));
|
||||
MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(100, 100, 255));
|
||||
MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100));
|
||||
|
@ -227,21 +230,68 @@ void RasterizerOpenGL::DrawTriangles() {
|
|||
MICROPROFILE_SCOPE(OpenGL_Drawing);
|
||||
const auto& regs = Pica::g_state.regs;
|
||||
|
||||
// Sync and bind the framebuffer surfaces
|
||||
CachedSurface* color_surface;
|
||||
CachedSurface* depth_surface;
|
||||
MathUtil::Rectangle<int> rect;
|
||||
std::tie(color_surface, depth_surface, rect) =
|
||||
res_cache.GetFramebufferSurfaces(regs.framebuffer.framebuffer);
|
||||
const bool has_stencil =
|
||||
regs.framebuffer.framebuffer.depth_format == Pica::FramebufferRegs::DepthFormat::D24S8;
|
||||
|
||||
const bool write_color_fb =
|
||||
state.color_mask.red_enabled == GL_TRUE || state.color_mask.green_enabled == GL_TRUE ||
|
||||
state.color_mask.blue_enabled == GL_TRUE || state.color_mask.alpha_enabled == GL_TRUE;
|
||||
|
||||
const bool write_depth_fb =
|
||||
(state.depth.test_enabled && state.depth.write_mask == GL_TRUE) ||
|
||||
(has_stencil && state.stencil.test_enabled && state.stencil.write_mask != 0);
|
||||
|
||||
const bool using_color_fb =
|
||||
regs.framebuffer.framebuffer.GetColorBufferPhysicalAddress() != 0 && write_color_fb;
|
||||
const bool using_depth_fb =
|
||||
regs.framebuffer.framebuffer.GetDepthBufferPhysicalAddress() != 0 &&
|
||||
(write_depth_fb || regs.framebuffer.output_merger.depth_test_enable != 0 ||
|
||||
(has_stencil && state.stencil.test_enabled));
|
||||
|
||||
MathUtil::Rectangle<s32> viewport_rect_unscaled{
|
||||
// These registers hold half-width and half-height, so must be multiplied by 2
|
||||
regs.rasterizer.viewport_corner.x, // left
|
||||
regs.rasterizer.viewport_corner.y + // top
|
||||
static_cast<s32>(Pica::float24::FromRaw(regs.rasterizer.viewport_size_y).ToFloat32() *
|
||||
2),
|
||||
regs.rasterizer.viewport_corner.x + // right
|
||||
static_cast<s32>(Pica::float24::FromRaw(regs.rasterizer.viewport_size_x).ToFloat32() *
|
||||
2),
|
||||
regs.rasterizer.viewport_corner.y // bottom
|
||||
};
|
||||
|
||||
Surface color_surface;
|
||||
Surface depth_surface;
|
||||
MathUtil::Rectangle<u32> surfaces_rect;
|
||||
std::tie(color_surface, depth_surface, surfaces_rect) =
|
||||
res_cache.GetFramebufferSurfaces(using_color_fb, using_depth_fb, viewport_rect_unscaled);
|
||||
|
||||
const u16 res_scale = color_surface != nullptr
|
||||
? color_surface->res_scale
|
||||
: (depth_surface == nullptr ? 1u : depth_surface->res_scale);
|
||||
|
||||
MathUtil::Rectangle<u32> draw_rect{
|
||||
static_cast<u32>(MathUtil::Clamp<s32>(static_cast<s32>(surfaces_rect.left) +
|
||||
viewport_rect_unscaled.left * res_scale,
|
||||
surfaces_rect.left, surfaces_rect.right)), // Left
|
||||
static_cast<u32>(MathUtil::Clamp<s32>(static_cast<s32>(surfaces_rect.bottom) +
|
||||
viewport_rect_unscaled.top * res_scale,
|
||||
surfaces_rect.bottom, surfaces_rect.top)), // Top
|
||||
static_cast<u32>(MathUtil::Clamp<s32>(static_cast<s32>(surfaces_rect.left) +
|
||||
viewport_rect_unscaled.right * res_scale,
|
||||
surfaces_rect.left, surfaces_rect.right)), // Right
|
||||
static_cast<u32>(MathUtil::Clamp<s32>(static_cast<s32>(surfaces_rect.bottom) +
|
||||
viewport_rect_unscaled.bottom * res_scale,
|
||||
surfaces_rect.bottom, surfaces_rect.top))}; // Bottom
|
||||
|
||||
// Bind the framebuffer surfaces
|
||||
state.draw.draw_framebuffer = framebuffer.handle;
|
||||
state.Apply();
|
||||
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
color_surface != nullptr ? color_surface->texture.handle : 0, 0);
|
||||
if (depth_surface != nullptr) {
|
||||
if (regs.framebuffer.framebuffer.depth_format ==
|
||||
Pica::FramebufferRegs::DepthFormat::D24S8) {
|
||||
if (has_stencil) {
|
||||
// attach both depth and stencil
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
|
||||
depth_surface->texture.handle, 0);
|
||||
|
@ -259,38 +309,30 @@ void RasterizerOpenGL::DrawTriangles() {
|
|||
}
|
||||
|
||||
// Sync the viewport
|
||||
// These registers hold half-width and half-height, so must be multiplied by 2
|
||||
GLsizei viewport_width =
|
||||
(GLsizei)Pica::float24::FromRaw(regs.rasterizer.viewport_size_x).ToFloat32() * 2;
|
||||
GLsizei viewport_height =
|
||||
(GLsizei)Pica::float24::FromRaw(regs.rasterizer.viewport_size_y).ToFloat32() * 2;
|
||||
state.viewport.x =
|
||||
static_cast<GLint>(surfaces_rect.left) + viewport_rect_unscaled.left * res_scale;
|
||||
state.viewport.y =
|
||||
static_cast<GLint>(surfaces_rect.bottom) + viewport_rect_unscaled.bottom * res_scale;
|
||||
state.viewport.width = static_cast<GLsizei>(viewport_rect_unscaled.GetWidth() * res_scale);
|
||||
state.viewport.height = static_cast<GLsizei>(viewport_rect_unscaled.GetHeight() * res_scale);
|
||||
|
||||
glViewport(
|
||||
(GLint)(rect.left + regs.rasterizer.viewport_corner.x * color_surface->res_scale_width),
|
||||
(GLint)(rect.bottom + regs.rasterizer.viewport_corner.y * color_surface->res_scale_height),
|
||||
(GLsizei)(viewport_width * color_surface->res_scale_width),
|
||||
(GLsizei)(viewport_height * color_surface->res_scale_height));
|
||||
|
||||
if (uniform_block_data.data.framebuffer_scale[0] != color_surface->res_scale_width ||
|
||||
uniform_block_data.data.framebuffer_scale[1] != color_surface->res_scale_height) {
|
||||
|
||||
uniform_block_data.data.framebuffer_scale[0] = color_surface->res_scale_width;
|
||||
uniform_block_data.data.framebuffer_scale[1] = color_surface->res_scale_height;
|
||||
if (uniform_block_data.data.framebuffer_scale != res_scale) {
|
||||
uniform_block_data.data.framebuffer_scale = res_scale;
|
||||
uniform_block_data.dirty = true;
|
||||
}
|
||||
|
||||
// Scissor checks are window-, not viewport-relative, which means that if the cached texture
|
||||
// sub-rect changes, the scissor bounds also need to be updated.
|
||||
GLint scissor_x1 = static_cast<GLint>(
|
||||
rect.left + regs.rasterizer.scissor_test.x1 * color_surface->res_scale_width);
|
||||
GLint scissor_y1 = static_cast<GLint>(
|
||||
rect.bottom + regs.rasterizer.scissor_test.y1 * color_surface->res_scale_height);
|
||||
GLint scissor_x1 =
|
||||
static_cast<GLint>(surfaces_rect.left + regs.rasterizer.scissor_test.x1 * res_scale);
|
||||
GLint scissor_y1 =
|
||||
static_cast<GLint>(surfaces_rect.bottom + regs.rasterizer.scissor_test.y1 * res_scale);
|
||||
// x2, y2 have +1 added to cover the entire pixel area, otherwise you might get cracks when
|
||||
// scaling or doing multisampling.
|
||||
GLint scissor_x2 = static_cast<GLint>(
|
||||
rect.left + (regs.rasterizer.scissor_test.x2 + 1) * color_surface->res_scale_width);
|
||||
GLint scissor_y2 = static_cast<GLint>(
|
||||
rect.bottom + (regs.rasterizer.scissor_test.y2 + 1) * color_surface->res_scale_height);
|
||||
GLint scissor_x2 =
|
||||
static_cast<GLint>(surfaces_rect.left + (regs.rasterizer.scissor_test.x2 + 1) * res_scale);
|
||||
GLint scissor_y2 = static_cast<GLint>(surfaces_rect.bottom +
|
||||
(regs.rasterizer.scissor_test.y2 + 1) * res_scale);
|
||||
|
||||
if (uniform_block_data.data.scissor_x1 != scissor_x1 ||
|
||||
uniform_block_data.data.scissor_x2 != scissor_x2 ||
|
||||
|
@ -311,7 +353,7 @@ void RasterizerOpenGL::DrawTriangles() {
|
|||
|
||||
if (texture.enabled) {
|
||||
texture_samplers[texture_index].SyncWithConfig(texture.config);
|
||||
CachedSurface* surface = res_cache.GetTextureSurface(texture);
|
||||
Surface surface = res_cache.GetTextureSurface(texture);
|
||||
if (surface != nullptr) {
|
||||
state.texture_units[texture_index].texture_2d = surface->texture.handle;
|
||||
} else {
|
||||
|
@ -380,6 +422,15 @@ void RasterizerOpenGL::DrawTriangles() {
|
|||
uniform_block_data.dirty = false;
|
||||
}
|
||||
|
||||
// Viewport can have negative offsets or larger
|
||||
// dimensions than our framebuffer sub-rect.
|
||||
// Enable scissor test to prevent drawing
|
||||
// outside of the framebuffer region
|
||||
state.scissor.enabled = true;
|
||||
state.scissor.x = draw_rect.left;
|
||||
state.scissor.y = draw_rect.bottom;
|
||||
state.scissor.width = draw_rect.GetWidth();
|
||||
state.scissor.height = draw_rect.GetHeight();
|
||||
state.Apply();
|
||||
|
||||
// Draw the vertex batch
|
||||
|
@ -387,16 +438,8 @@ void RasterizerOpenGL::DrawTriangles() {
|
|||
GL_STREAM_DRAW);
|
||||
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)vertex_batch.size());
|
||||
|
||||
// Mark framebuffer surfaces as dirty
|
||||
// TODO: Restrict invalidation area to the viewport
|
||||
if (color_surface != nullptr) {
|
||||
color_surface->dirty = true;
|
||||
res_cache.FlushRegion(color_surface->addr, color_surface->size, color_surface, true);
|
||||
}
|
||||
if (depth_surface != nullptr) {
|
||||
depth_surface->dirty = true;
|
||||
res_cache.FlushRegion(depth_surface->addr, depth_surface->size, depth_surface, true);
|
||||
}
|
||||
// Disable scissor test
|
||||
state.scissor.enabled = false;
|
||||
|
||||
vertex_batch.clear();
|
||||
|
||||
|
@ -405,6 +448,22 @@ void RasterizerOpenGL::DrawTriangles() {
|
|||
state.texture_units[texture_index].texture_2d = 0;
|
||||
}
|
||||
state.Apply();
|
||||
|
||||
// Mark framebuffer surfaces as dirty
|
||||
MathUtil::Rectangle<u32> draw_rect_unscaled{
|
||||
draw_rect.left / res_scale, draw_rect.top / res_scale, draw_rect.right / res_scale,
|
||||
draw_rect.bottom / res_scale};
|
||||
|
||||
if (color_surface != nullptr && write_color_fb) {
|
||||
auto interval = color_surface->GetSubRectInterval(draw_rect_unscaled);
|
||||
res_cache.InvalidateRegion(boost::icl::first(interval), boost::icl::length(interval),
|
||||
color_surface);
|
||||
}
|
||||
if (depth_surface != nullptr && write_depth_fb) {
|
||||
auto interval = depth_surface->GetSubRectInterval(draw_rect_unscaled);
|
||||
res_cache.InvalidateRegion(boost::icl::first(interval), boost::icl::length(interval),
|
||||
depth_surface);
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {
|
||||
|
@ -893,227 +952,164 @@ void RasterizerOpenGL::FlushAll() {
|
|||
|
||||
void RasterizerOpenGL::FlushRegion(PAddr addr, u32 size) {
|
||||
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
|
||||
res_cache.FlushRegion(addr, size, nullptr, false);
|
||||
res_cache.FlushRegion(addr, size);
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::InvalidateRegion(PAddr addr, u32 size) {
|
||||
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
|
||||
res_cache.InvalidateRegion(addr, size, nullptr);
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::FlushAndInvalidateRegion(PAddr addr, u32 size) {
|
||||
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
|
||||
res_cache.FlushRegion(addr, size, nullptr, true);
|
||||
res_cache.FlushRegion(addr, size);
|
||||
res_cache.InvalidateRegion(addr, size, nullptr);
|
||||
}
|
||||
|
||||
bool RasterizerOpenGL::AccelerateDisplayTransfer(const GPU::Regs::DisplayTransferConfig& config) {
|
||||
MICROPROFILE_SCOPE(OpenGL_Blits);
|
||||
|
||||
CachedSurface src_params;
|
||||
SurfaceParams src_params;
|
||||
src_params.addr = config.GetPhysicalInputAddress();
|
||||
// It's important to use the correct source input width to properly skip over parts of the input
|
||||
// image which will be cropped from the output but still affect the stride of the input image.
|
||||
src_params.width = config.input_width;
|
||||
// Using the output's height is fine because we don't read or skip over the remaining part of
|
||||
// the image, and it allows for smaller texture cache lookup rectangles.
|
||||
src_params.width = config.output_width;
|
||||
src_params.stride = config.input_width;
|
||||
src_params.height = config.output_height;
|
||||
src_params.is_tiled = !config.input_linear;
|
||||
src_params.pixel_format = CachedSurface::PixelFormatFromGPUPixelFormat(config.input_format);
|
||||
src_params.pixel_format = SurfaceParams::PixelFormatFromGPUPixelFormat(config.input_format);
|
||||
src_params.UpdateParams();
|
||||
|
||||
CachedSurface dst_params;
|
||||
SurfaceParams dst_params;
|
||||
dst_params.addr = config.GetPhysicalOutputAddress();
|
||||
dst_params.width =
|
||||
config.scaling != config.NoScale ? config.output_width / 2 : config.output_width.Value();
|
||||
dst_params.height =
|
||||
config.scaling == config.ScaleXY ? config.output_height / 2 : config.output_height.Value();
|
||||
dst_params.width = config.scaling != config.NoScale ? config.output_width.Value() / 2
|
||||
: config.output_width.Value();
|
||||
dst_params.height = config.scaling == config.ScaleXY ? config.output_height.Value() / 2
|
||||
: config.output_height.Value();
|
||||
dst_params.is_tiled = config.input_linear != config.dont_swizzle;
|
||||
dst_params.pixel_format = CachedSurface::PixelFormatFromGPUPixelFormat(config.output_format);
|
||||
dst_params.pixel_format = SurfaceParams::PixelFormatFromGPUPixelFormat(config.output_format);
|
||||
dst_params.UpdateParams();
|
||||
|
||||
MathUtil::Rectangle<int> src_rect;
|
||||
CachedSurface* src_surface = res_cache.GetSurfaceRect(src_params, false, true, src_rect);
|
||||
|
||||
if (src_surface == nullptr) {
|
||||
MathUtil::Rectangle<u32> src_rect;
|
||||
Surface src_surface;
|
||||
std::tie(src_surface, src_rect) =
|
||||
res_cache.GetSurfaceSubRect(src_params, ScaleMatch::Ignore, true);
|
||||
if (src_surface == nullptr)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Adjust the source rectangle to take into account parts of the input lines being cropped
|
||||
if (config.input_width > config.output_width) {
|
||||
src_rect.right -= static_cast<int>((config.input_width - config.output_width) *
|
||||
src_surface->res_scale_width);
|
||||
}
|
||||
dst_params.res_scale = src_surface->res_scale;
|
||||
|
||||
// Require destination surface to have same resolution scale as source to preserve scaling
|
||||
dst_params.res_scale_width = src_surface->res_scale_width;
|
||||
dst_params.res_scale_height = src_surface->res_scale_height;
|
||||
|
||||
MathUtil::Rectangle<int> dst_rect;
|
||||
CachedSurface* dst_surface = res_cache.GetSurfaceRect(dst_params, true, false, dst_rect);
|
||||
|
||||
if (dst_surface == nullptr) {
|
||||
MathUtil::Rectangle<u32> dst_rect;
|
||||
Surface dst_surface;
|
||||
std::tie(dst_surface, dst_rect) =
|
||||
res_cache.GetSurfaceSubRect(dst_params, ScaleMatch::Upscale, false);
|
||||
if (dst_surface == nullptr)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't accelerate if the src and dst surfaces are the same
|
||||
if (src_surface == dst_surface) {
|
||||
if (src_surface->is_tiled != dst_surface->is_tiled)
|
||||
std::swap(src_rect.top, src_rect.bottom);
|
||||
|
||||
if (config.flip_vertically)
|
||||
std::swap(src_rect.top, src_rect.bottom);
|
||||
|
||||
if (!res_cache.BlitSurfaces(src_surface, src_rect, dst_surface, dst_rect))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config.flip_vertically) {
|
||||
std::swap(dst_rect.top, dst_rect.bottom);
|
||||
}
|
||||
|
||||
if (!res_cache.TryBlitSurfaces(src_surface, src_rect, dst_surface, dst_rect)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 dst_size = dst_params.width * dst_params.height *
|
||||
CachedSurface::GetFormatBpp(dst_params.pixel_format) / 8;
|
||||
dst_surface->dirty = true;
|
||||
res_cache.FlushRegion(config.GetPhysicalOutputAddress(), dst_size, dst_surface, true);
|
||||
res_cache.InvalidateRegion(dst_params.addr, dst_params.size, dst_surface);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RasterizerOpenGL::AccelerateTextureCopy(const GPU::Regs::DisplayTransferConfig& config) {
|
||||
// TODO(tfarley): Try to hardware accelerate this
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RasterizerOpenGL::AccelerateFill(const GPU::Regs::MemoryFillConfig& config) {
|
||||
MICROPROFILE_SCOPE(OpenGL_Blits);
|
||||
using PixelFormat = CachedSurface::PixelFormat;
|
||||
using SurfaceType = CachedSurface::SurfaceType;
|
||||
|
||||
CachedSurface* dst_surface = res_cache.TryGetFillSurface(config);
|
||||
|
||||
if (dst_surface == nullptr) {
|
||||
u32 copy_size = Common::AlignDown(config.texture_copy.size, 16);
|
||||
if (copy_size == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
OpenGLState cur_state = OpenGLState::GetCurState();
|
||||
|
||||
SurfaceType dst_type = CachedSurface::GetFormatType(dst_surface->pixel_format);
|
||||
|
||||
GLuint old_fb = cur_state.draw.draw_framebuffer;
|
||||
cur_state.draw.draw_framebuffer = framebuffer.handle;
|
||||
// TODO: When scissor test is implemented, need to disable scissor test in cur_state here so
|
||||
// Clear call isn't affected
|
||||
cur_state.Apply();
|
||||
|
||||
if (dst_type == SurfaceType::Color || dst_type == SurfaceType::Texture) {
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
dst_surface->texture.handle, 0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
|
||||
0);
|
||||
|
||||
GLfloat color_values[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
|
||||
// TODO: Handle additional pixel format and fill value size combinations to accelerate more
|
||||
// cases
|
||||
// For instance, checking if fill value's bytes/bits repeat to allow filling
|
||||
// I8/A8/I4/A4/...
|
||||
// Currently only handles formats that are multiples of the fill value size
|
||||
|
||||
if (config.fill_24bit) {
|
||||
switch (dst_surface->pixel_format) {
|
||||
case PixelFormat::RGB8:
|
||||
color_values[0] = config.value_24bit_r / 255.0f;
|
||||
color_values[1] = config.value_24bit_g / 255.0f;
|
||||
color_values[2] = config.value_24bit_b / 255.0f;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
} else if (config.fill_32bit) {
|
||||
u32 value = config.value_32bit;
|
||||
|
||||
switch (dst_surface->pixel_format) {
|
||||
case PixelFormat::RGBA8:
|
||||
color_values[0] = (value >> 24) / 255.0f;
|
||||
color_values[1] = ((value >> 16) & 0xFF) / 255.0f;
|
||||
color_values[2] = ((value >> 8) & 0xFF) / 255.0f;
|
||||
color_values[3] = (value & 0xFF) / 255.0f;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
u16 value_16bit = config.value_16bit.Value();
|
||||
Math::Vec4<u8> color;
|
||||
|
||||
switch (dst_surface->pixel_format) {
|
||||
case PixelFormat::RGBA8:
|
||||
color_values[0] = (value_16bit >> 8) / 255.0f;
|
||||
color_values[1] = (value_16bit & 0xFF) / 255.0f;
|
||||
color_values[2] = color_values[0];
|
||||
color_values[3] = color_values[1];
|
||||
break;
|
||||
case PixelFormat::RGB5A1:
|
||||
color = Color::DecodeRGB5A1((const u8*)&value_16bit);
|
||||
color_values[0] = color[0] / 31.0f;
|
||||
color_values[1] = color[1] / 31.0f;
|
||||
color_values[2] = color[2] / 31.0f;
|
||||
color_values[3] = color[3];
|
||||
break;
|
||||
case PixelFormat::RGB565:
|
||||
color = Color::DecodeRGB565((const u8*)&value_16bit);
|
||||
color_values[0] = color[0] / 31.0f;
|
||||
color_values[1] = color[1] / 63.0f;
|
||||
color_values[2] = color[2] / 31.0f;
|
||||
break;
|
||||
case PixelFormat::RGBA4:
|
||||
color = Color::DecodeRGBA4((const u8*)&value_16bit);
|
||||
color_values[0] = color[0] / 15.0f;
|
||||
color_values[1] = color[1] / 15.0f;
|
||||
color_values[2] = color[2] / 15.0f;
|
||||
color_values[3] = color[3] / 15.0f;
|
||||
break;
|
||||
case PixelFormat::IA8:
|
||||
case PixelFormat::RG8:
|
||||
color_values[0] = (value_16bit >> 8) / 255.0f;
|
||||
color_values[1] = (value_16bit & 0xFF) / 255.0f;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
cur_state.color_mask.red_enabled = GL_TRUE;
|
||||
cur_state.color_mask.green_enabled = GL_TRUE;
|
||||
cur_state.color_mask.blue_enabled = GL_TRUE;
|
||||
cur_state.color_mask.alpha_enabled = GL_TRUE;
|
||||
cur_state.Apply();
|
||||
glClearBufferfv(GL_COLOR, 0, color_values);
|
||||
} else if (dst_type == SurfaceType::Depth) {
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
|
||||
dst_surface->texture.handle, 0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
|
||||
|
||||
GLfloat value_float;
|
||||
if (dst_surface->pixel_format == CachedSurface::PixelFormat::D16) {
|
||||
value_float = config.value_32bit / 65535.0f; // 2^16 - 1
|
||||
} else if (dst_surface->pixel_format == CachedSurface::PixelFormat::D24) {
|
||||
value_float = config.value_32bit / 16777215.0f; // 2^24 - 1
|
||||
}
|
||||
|
||||
cur_state.depth.write_mask = GL_TRUE;
|
||||
cur_state.Apply();
|
||||
glClearBufferfv(GL_DEPTH, 0, &value_float);
|
||||
} else if (dst_type == 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,
|
||||
dst_surface->texture.handle, 0);
|
||||
|
||||
GLfloat value_float = (config.value_32bit & 0xFFFFFF) / 16777215.0f; // 2^24 - 1
|
||||
GLint value_int = (config.value_32bit >> 24);
|
||||
|
||||
cur_state.depth.write_mask = GL_TRUE;
|
||||
cur_state.stencil.write_mask = 0xFF;
|
||||
cur_state.Apply();
|
||||
glClearBufferfi(GL_DEPTH_STENCIL, 0, value_float, value_int);
|
||||
u32 input_gap = config.texture_copy.input_gap * 16;
|
||||
u32 input_width = config.texture_copy.input_width * 16;
|
||||
if (input_width == 0 && input_gap != 0) {
|
||||
return false;
|
||||
}
|
||||
if (input_gap == 0 || input_width >= copy_size) {
|
||||
input_width = copy_size;
|
||||
input_gap = 0;
|
||||
}
|
||||
if (copy_size % input_width != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
cur_state.draw.draw_framebuffer = old_fb;
|
||||
// TODO: Return scissor test to previous value when scissor test is implemented
|
||||
cur_state.Apply();
|
||||
u32 output_gap = config.texture_copy.output_gap * 16;
|
||||
u32 output_width = config.texture_copy.output_width * 16;
|
||||
if (output_width == 0 && output_gap != 0) {
|
||||
return false;
|
||||
}
|
||||
if (output_gap == 0 || output_width >= copy_size) {
|
||||
output_width = copy_size;
|
||||
output_gap = 0;
|
||||
}
|
||||
if (copy_size % output_width != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dst_surface->dirty = true;
|
||||
res_cache.FlushRegion(dst_surface->addr, dst_surface->size, dst_surface, true);
|
||||
SurfaceParams src_params;
|
||||
src_params.addr = config.GetPhysicalInputAddress();
|
||||
src_params.stride = input_width + input_gap; // stride in bytes
|
||||
src_params.width = input_width; // width in bytes
|
||||
src_params.height = copy_size / input_width;
|
||||
src_params.size = ((src_params.height - 1) * src_params.stride) + src_params.width;
|
||||
src_params.end = src_params.addr + src_params.size;
|
||||
|
||||
MathUtil::Rectangle<u32> src_rect;
|
||||
Surface src_surface;
|
||||
std::tie(src_surface, src_rect) = res_cache.GetTexCopySurface(src_params);
|
||||
if (src_surface == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (output_gap != 0 &&
|
||||
(output_width !=
|
||||
src_surface->BytesInPixels(src_rect.GetWidth() / src_surface->res_scale) *
|
||||
(src_surface->is_tiled ? 8 : 1) ||
|
||||
output_gap % src_surface->BytesInPixels(src_surface->is_tiled ? 64 : 1) != 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SurfaceParams dst_params = *src_surface;
|
||||
dst_params.addr = config.GetPhysicalOutputAddress();
|
||||
dst_params.width = src_rect.GetWidth() / src_surface->res_scale;
|
||||
dst_params.stride =
|
||||
dst_params.width +
|
||||
src_surface->PixelsInBytes(src_surface->is_tiled ? output_gap / 8 : output_gap);
|
||||
dst_params.height = src_rect.GetHeight() / src_surface->res_scale;
|
||||
dst_params.res_scale = src_surface->res_scale;
|
||||
dst_params.UpdateParams();
|
||||
|
||||
// Since we are going to invalidate the gap if there is one, we will have to load it first
|
||||
const bool load_gap = output_gap != 0;
|
||||
MathUtil::Rectangle<u32> dst_rect;
|
||||
Surface dst_surface;
|
||||
std::tie(dst_surface, dst_rect) =
|
||||
res_cache.GetSurfaceSubRect(dst_params, ScaleMatch::Upscale, load_gap);
|
||||
if (src_surface == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dst_surface->type == SurfaceType::Texture) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!res_cache.BlitSurfaces(src_surface, src_rect, dst_surface, dst_rect)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
res_cache.InvalidateRegion(dst_params.addr, dst_params.size, dst_surface);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RasterizerOpenGL::AccelerateFill(const GPU::Regs::MemoryFillConfig& config) {
|
||||
Surface dst_surface = res_cache.GetFillSurface(config);
|
||||
if (dst_surface == nullptr)
|
||||
return false;
|
||||
|
||||
res_cache.InvalidateRegion(dst_surface->addr, dst_surface->size, dst_surface);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1125,16 +1121,19 @@ bool RasterizerOpenGL::AccelerateDisplay(const GPU::Regs::FramebufferConfig& con
|
|||
}
|
||||
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
|
||||
|
||||
CachedSurface src_params;
|
||||
SurfaceParams src_params;
|
||||
src_params.addr = framebuffer_addr;
|
||||
src_params.width = config.width;
|
||||
src_params.width = std::min(config.width.Value(), pixel_stride);
|
||||
src_params.height = config.height;
|
||||
src_params.pixel_stride = pixel_stride;
|
||||
src_params.stride = pixel_stride;
|
||||
src_params.is_tiled = false;
|
||||
src_params.pixel_format = CachedSurface::PixelFormatFromGPUPixelFormat(config.color_format);
|
||||
src_params.pixel_format = SurfaceParams::PixelFormatFromGPUPixelFormat(config.color_format);
|
||||
src_params.UpdateParams();
|
||||
|
||||
MathUtil::Rectangle<int> src_rect;
|
||||
CachedSurface* src_surface = res_cache.GetSurfaceRect(src_params, false, true, src_rect);
|
||||
MathUtil::Rectangle<u32> src_rect;
|
||||
Surface src_surface;
|
||||
std::tie(src_surface, src_rect) =
|
||||
res_cache.GetSurfaceSubRect(src_params, ScaleMatch::Ignore, true);
|
||||
|
||||
if (src_surface == nullptr) {
|
||||
return false;
|
||||
|
@ -1144,8 +1143,8 @@ bool RasterizerOpenGL::AccelerateDisplay(const GPU::Regs::FramebufferConfig& con
|
|||
u32 scaled_height = src_surface->GetScaledHeight();
|
||||
|
||||
screen_info.display_texcoords = MathUtil::Rectangle<float>(
|
||||
(float)src_rect.top / (float)scaled_height, (float)src_rect.left / (float)scaled_width,
|
||||
(float)src_rect.bottom / (float)scaled_height, (float)src_rect.right / (float)scaled_width);
|
||||
(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->texture.handle;
|
||||
|
||||
|
@ -1158,8 +1157,8 @@ void RasterizerOpenGL::SamplerInfo::Create() {
|
|||
wrap_s = wrap_t = TextureConfig::Repeat;
|
||||
border_color = 0;
|
||||
|
||||
glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER,
|
||||
GL_LINEAR); // default is GL_LINEAR_MIPMAP_LINEAR
|
||||
// default is GL_LINEAR_MIPMAP_LINEAR
|
||||
glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
// Other attributes have correct defaults
|
||||
}
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@ public:
|
|||
void NotifyPicaRegisterChanged(u32 id) override;
|
||||
void FlushAll() override;
|
||||
void FlushRegion(PAddr addr, u32 size) override;
|
||||
void InvalidateRegion(PAddr addr, u32 size) override;
|
||||
void FlushAndInvalidateRegion(PAddr addr, u32 size) override;
|
||||
bool AccelerateDisplayTransfer(const GPU::Regs::DisplayTransferConfig& config) override;
|
||||
bool AccelerateTextureCopy(const GPU::Regs::DisplayTransferConfig& config) override;
|
||||
|
@ -135,7 +136,7 @@ private:
|
|||
// the end of a uniform block is included in UNIFORM_BLOCK_DATA_SIZE or not.
|
||||
// Not following that rule will cause problems on some AMD drivers.
|
||||
struct UniformData {
|
||||
alignas(8) GLvec2 framebuffer_scale;
|
||||
GLint framebuffer_scale;
|
||||
GLint alphatest_ref;
|
||||
GLfloat depth_scale;
|
||||
GLfloat depth_offset;
|
||||
|
@ -155,7 +156,7 @@ private:
|
|||
};
|
||||
|
||||
static_assert(
|
||||
sizeof(UniformData) == 0x470,
|
||||
sizeof(UniformData) == 0x460,
|
||||
"The size of the UniformData structure has changed, update the structure in the shader");
|
||||
static_assert(sizeof(UniformData) < 16384,
|
||||
"UniformData structure must be less than 16kb as per the OpenGL spec");
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -13,6 +13,7 @@
|
|||
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
|
||||
#endif
|
||||
#include <boost/icl/interval_map.hpp>
|
||||
#include <boost/icl/interval_set.hpp>
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
@ -20,21 +21,37 @@
|
|||
#include "common/assert.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/math_util.h"
|
||||
#include "core/hw/gpu.h"
|
||||
#include "video_core/regs_framebuffer.h"
|
||||
#include "video_core/regs_texturing.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
|
||||
namespace MathUtil {
|
||||
template <class T>
|
||||
struct Rectangle;
|
||||
}
|
||||
|
||||
struct CachedSurface;
|
||||
using Surface = std::shared_ptr<CachedSurface>;
|
||||
using SurfaceSet = std::set<Surface>;
|
||||
|
||||
using SurfaceCache = boost::icl::interval_map<PAddr, std::set<std::shared_ptr<CachedSurface>>>;
|
||||
using SurfaceRegions = boost::icl::interval_set<PAddr>;
|
||||
using SurfaceMap = boost::icl::interval_map<PAddr, Surface>;
|
||||
using SurfaceCache = boost::icl::interval_map<PAddr, SurfaceSet>;
|
||||
|
||||
struct CachedSurface {
|
||||
using SurfaceInterval = SurfaceCache::interval_type;
|
||||
static_assert(std::is_same<SurfaceRegions::interval_type, SurfaceCache::interval_type>() &&
|
||||
std::is_same<SurfaceMap::interval_type, SurfaceCache::interval_type>(),
|
||||
"incorrect interval types");
|
||||
|
||||
using SurfaceRect_Tuple = std::tuple<Surface, MathUtil::Rectangle<u32>>;
|
||||
using SurfaceSurfaceRect_Tuple = std::tuple<Surface, Surface, MathUtil::Rectangle<u32>>;
|
||||
|
||||
using PageMap = boost::icl::interval_map<u32, int>;
|
||||
|
||||
enum class ScaleMatch {
|
||||
Exact, // only accept same res scale
|
||||
Upscale, // only allow higher scale than params
|
||||
Ignore // accept every scaled res
|
||||
};
|
||||
|
||||
struct SurfaceParams {
|
||||
enum class PixelFormat {
|
||||
// First 5 formats are shared between textures and color buffers
|
||||
RGBA8 = 0,
|
||||
|
@ -68,11 +85,12 @@ struct CachedSurface {
|
|||
Texture = 1,
|
||||
Depth = 2,
|
||||
DepthStencil = 3,
|
||||
Invalid = 4,
|
||||
Fill = 4,
|
||||
Invalid = 5
|
||||
};
|
||||
|
||||
static unsigned int GetFormatBpp(CachedSurface::PixelFormat format) {
|
||||
static const std::array<unsigned int, 18> bpp_table = {
|
||||
static constexpr unsigned int GetFormatBpp(PixelFormat format) {
|
||||
constexpr std::array<unsigned int, 18> bpp_table = {
|
||||
32, // RGBA8
|
||||
24, // RGB8
|
||||
16, // RGB5A1
|
||||
|
@ -93,8 +111,11 @@ struct CachedSurface {
|
|||
32, // D24S8
|
||||
};
|
||||
|
||||
ASSERT((unsigned int)format < ARRAY_SIZE(bpp_table));
|
||||
return bpp_table[(unsigned int)format];
|
||||
assert(static_cast<size_t>(format) < bpp_table.size());
|
||||
return bpp_table[static_cast<size_t>(format)];
|
||||
}
|
||||
unsigned int GetFormatBpp() const {
|
||||
return GetFormatBpp(pixel_format);
|
||||
}
|
||||
|
||||
static PixelFormat PixelFormatFromTextureFormat(Pica::TexturingRegs::TextureFormat format) {
|
||||
|
@ -142,7 +163,7 @@ struct CachedSurface {
|
|||
return false;
|
||||
}
|
||||
|
||||
static SurfaceType GetFormatType(PixelFormat pixel_format) {
|
||||
static constexpr SurfaceType GetFormatType(PixelFormat pixel_format) {
|
||||
if ((unsigned int)pixel_format < 5) {
|
||||
return SurfaceType::Color;
|
||||
}
|
||||
|
@ -162,31 +183,117 @@ struct CachedSurface {
|
|||
return SurfaceType::Invalid;
|
||||
}
|
||||
|
||||
/// Update the params "size", "end" and "type" from the already set "addr", "width", "height"
|
||||
/// and "pixel_format"
|
||||
void UpdateParams() {
|
||||
if (stride == 0) {
|
||||
stride = width;
|
||||
}
|
||||
type = GetFormatType(pixel_format);
|
||||
size = !is_tiled ? BytesInPixels(stride * (height - 1) + width)
|
||||
: BytesInPixels(stride * 8 * (height / 8 - 1) + width * 8);
|
||||
end = addr + size;
|
||||
}
|
||||
|
||||
SurfaceInterval GetInterval() const {
|
||||
return SurfaceInterval::right_open(addr, end);
|
||||
}
|
||||
|
||||
// Returns the outer rectangle containing "interval"
|
||||
SurfaceParams FromInterval(SurfaceInterval interval) const;
|
||||
|
||||
SurfaceInterval GetSubRectInterval(MathUtil::Rectangle<u32> unscaled_rect) const;
|
||||
|
||||
// Returns the region of the biggest valid rectange within interval
|
||||
SurfaceInterval GetCopyableInterval(const Surface& src_surface) const;
|
||||
|
||||
u32 GetScaledWidth() const {
|
||||
return (u32)(width * res_scale_width);
|
||||
return width * res_scale;
|
||||
}
|
||||
|
||||
u32 GetScaledHeight() const {
|
||||
return (u32)(height * res_scale_height);
|
||||
return height * res_scale;
|
||||
}
|
||||
|
||||
PAddr addr;
|
||||
u32 size;
|
||||
MathUtil::Rectangle<u32> GetRect() const {
|
||||
return {0, height, width, 0};
|
||||
}
|
||||
|
||||
PAddr min_valid;
|
||||
PAddr max_valid;
|
||||
MathUtil::Rectangle<u32> GetScaledRect() const {
|
||||
return {0, GetScaledHeight(), GetScaledWidth(), 0};
|
||||
}
|
||||
|
||||
u32 PixelsInBytes(u32 size) const {
|
||||
return size * CHAR_BIT / GetFormatBpp(pixel_format);
|
||||
}
|
||||
|
||||
u32 BytesInPixels(u32 pixels) const {
|
||||
return pixels * GetFormatBpp(pixel_format) / CHAR_BIT;
|
||||
}
|
||||
|
||||
bool ExactMatch(const SurfaceParams& other_surface) const;
|
||||
bool CanSubRect(const SurfaceParams& sub_surface) const;
|
||||
bool CanExpand(const SurfaceParams& expanded_surface) const;
|
||||
bool CanTexCopy(const SurfaceParams& texcopy_params) const;
|
||||
|
||||
MathUtil::Rectangle<u32> GetSubRect(const SurfaceParams& sub_surface) const;
|
||||
MathUtil::Rectangle<u32> GetScaledSubRect(const SurfaceParams& sub_surface) const;
|
||||
|
||||
PAddr addr = 0;
|
||||
PAddr end = 0;
|
||||
u32 size = 0;
|
||||
|
||||
u32 width = 0;
|
||||
u32 height = 0;
|
||||
u32 stride = 0;
|
||||
u16 res_scale = 1;
|
||||
|
||||
bool is_tiled = false;
|
||||
PixelFormat pixel_format = PixelFormat::Invalid;
|
||||
SurfaceType type = SurfaceType::Invalid;
|
||||
};
|
||||
|
||||
struct CachedSurface : SurfaceParams {
|
||||
bool CanFill(const SurfaceParams& dest_surface, SurfaceInterval fill_interval) const;
|
||||
bool CanCopy(const SurfaceParams& dest_surface, SurfaceInterval copy_interval) const;
|
||||
|
||||
bool IsRegionValid(SurfaceInterval interval) const {
|
||||
return (invalid_regions.find(interval) == invalid_regions.end());
|
||||
}
|
||||
|
||||
bool IsSurfaceFullyInvalid() const {
|
||||
return (invalid_regions & GetInterval()) == SurfaceRegions(GetInterval());
|
||||
}
|
||||
|
||||
bool registered = false;
|
||||
SurfaceRegions invalid_regions;
|
||||
|
||||
u32 fill_size = 0; /// Number of bytes to read from fill_data
|
||||
std::array<u8, 4> fill_data;
|
||||
|
||||
OGLTexture texture;
|
||||
u32 width;
|
||||
u32 height;
|
||||
/// Stride between lines, in pixels. Only valid for images in linear format.
|
||||
u32 pixel_stride = 0;
|
||||
float res_scale_width = 1.f;
|
||||
float res_scale_height = 1.f;
|
||||
|
||||
bool is_tiled;
|
||||
PixelFormat pixel_format;
|
||||
bool dirty;
|
||||
static constexpr unsigned int GetGLBytesPerPixel(PixelFormat format) {
|
||||
// OpenGL needs 4 bpp alignment for D24 since using GL_UNSIGNED_INT as type
|
||||
return format == PixelFormat::Invalid
|
||||
? 0
|
||||
: (format == PixelFormat::D24 || GetFormatType(format) == SurfaceType::Texture)
|
||||
? 4
|
||||
: SurfaceParams::GetFormatBpp(format) / 8;
|
||||
}
|
||||
|
||||
std::unique_ptr<u8[]> gl_buffer;
|
||||
size_t gl_buffer_size = 0;
|
||||
|
||||
// Read/Write data in 3DS memory to/from gl_buffer
|
||||
void LoadGLBuffer(PAddr load_start, PAddr load_end);
|
||||
void FlushGLBuffer(PAddr flush_start, PAddr flush_end);
|
||||
|
||||
// Upload/Download data in gl_buffer in/to this surface's texture
|
||||
void UploadGLTexture(const MathUtil::Rectangle<u32>& rect, GLuint read_fb_handle,
|
||||
GLuint draw_fb_handle);
|
||||
void DownloadGLTexture(const MathUtil::Rectangle<u32>& rect, GLuint read_fb_handle,
|
||||
GLuint draw_fb_handle);
|
||||
};
|
||||
|
||||
class RasterizerCacheOpenGL : NonCopyable {
|
||||
|
@ -194,46 +301,78 @@ public:
|
|||
RasterizerCacheOpenGL();
|
||||
~RasterizerCacheOpenGL();
|
||||
|
||||
/// Blits one texture to another
|
||||
void BlitTextures(GLuint src_tex, GLuint dst_tex, CachedSurface::SurfaceType type,
|
||||
const MathUtil::Rectangle<int>& src_rect,
|
||||
const MathUtil::Rectangle<int>& dst_rect);
|
||||
/// Blit one surface's texture to another
|
||||
bool BlitSurfaces(const Surface& src_surface, const MathUtil::Rectangle<u32>& src_rect,
|
||||
const Surface& dst_surface, const MathUtil::Rectangle<u32>& dst_rect);
|
||||
|
||||
/// Attempt to blit one surface's texture to another
|
||||
bool TryBlitSurfaces(CachedSurface* src_surface, const MathUtil::Rectangle<int>& src_rect,
|
||||
CachedSurface* dst_surface, const MathUtil::Rectangle<int>& dst_rect);
|
||||
void ConvertD24S8toABGR(GLuint src_tex, const MathUtil::Rectangle<u32>& src_rect,
|
||||
GLuint dst_tex, const MathUtil::Rectangle<u32>& dst_rect);
|
||||
|
||||
/// Loads a texture from 3DS memory to OpenGL and caches it (if not already cached)
|
||||
CachedSurface* GetSurface(const CachedSurface& params, bool match_res_scale,
|
||||
bool load_if_create);
|
||||
/// Copy one surface's region to another
|
||||
void CopySurface(const Surface& src_surface, const Surface& dst_surface,
|
||||
SurfaceInterval copy_interval);
|
||||
|
||||
/// Load a texture from 3DS memory to OpenGL and cache it (if not already cached)
|
||||
Surface GetSurface(const SurfaceParams& params, ScaleMatch match_res_scale,
|
||||
bool load_if_create);
|
||||
|
||||
/// Attempt to find a subrect (resolution scaled) of a surface, otherwise loads a texture from
|
||||
/// 3DS memory to OpenGL and caches it (if not already cached)
|
||||
CachedSurface* GetSurfaceRect(const CachedSurface& params, bool match_res_scale,
|
||||
bool load_if_create, MathUtil::Rectangle<int>& out_rect);
|
||||
SurfaceRect_Tuple GetSurfaceSubRect(const SurfaceParams& params, ScaleMatch match_res_scale,
|
||||
bool load_if_create);
|
||||
|
||||
/// Gets a surface based on the texture configuration
|
||||
CachedSurface* GetTextureSurface(const Pica::TexturingRegs::FullTextureConfig& config);
|
||||
/// Get a surface based on the texture configuration
|
||||
Surface GetTextureSurface(const Pica::TexturingRegs::FullTextureConfig& config);
|
||||
|
||||
/// Gets the color and depth surfaces and rect (resolution scaled) based on the framebuffer
|
||||
/// configuration
|
||||
std::tuple<CachedSurface*, CachedSurface*, MathUtil::Rectangle<int>> GetFramebufferSurfaces(
|
||||
const Pica::FramebufferRegs::FramebufferConfig& config);
|
||||
/// Get the color and depth surfaces based on the framebuffer configuration
|
||||
SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb,
|
||||
const MathUtil::Rectangle<s32>& viewport_rect);
|
||||
|
||||
/// Attempt to get a surface that exactly matches the fill region and format
|
||||
CachedSurface* TryGetFillSurface(const GPU::Regs::MemoryFillConfig& config);
|
||||
/// Get a surface that matches the fill config
|
||||
Surface GetFillSurface(const GPU::Regs::MemoryFillConfig& config);
|
||||
|
||||
/// Write the surface back to memory
|
||||
void FlushSurface(CachedSurface* surface);
|
||||
/// Get a surface that matches a "texture copy" display transfer config
|
||||
SurfaceRect_Tuple GetTexCopySurface(const SurfaceParams& params);
|
||||
|
||||
/// Write any cached resources overlapping the region back to memory (if dirty) and optionally
|
||||
/// invalidate them in the cache
|
||||
void FlushRegion(PAddr addr, u32 size, const CachedSurface* skip_surface, bool invalidate);
|
||||
/// Write any cached resources overlapping the region back to memory (if dirty)
|
||||
void FlushRegion(PAddr addr, u32 size, Surface flush_surface = nullptr);
|
||||
|
||||
/// Mark region as being invalidated by region_owner (nullptr if 3DS memory)
|
||||
void InvalidateRegion(PAddr addr, u32 size, const Surface& region_owner);
|
||||
|
||||
/// Flush all cached resources tracked by this cache manager
|
||||
void FlushAll();
|
||||
|
||||
private:
|
||||
void DuplicateSurface(const Surface& src_surface, const Surface& dest_surface);
|
||||
|
||||
/// Update surface's texture for given region when necessary
|
||||
void ValidateSurface(const Surface& surface, PAddr addr, u32 size);
|
||||
|
||||
/// Create a new surface
|
||||
Surface CreateSurface(const SurfaceParams& params);
|
||||
|
||||
/// Register surface into the cache
|
||||
void RegisterSurface(const Surface& surface);
|
||||
|
||||
/// Remove surface from the cache
|
||||
void UnregisterSurface(const Surface& surface);
|
||||
|
||||
/// Increase/decrease the number of surface in pages touching the specified region
|
||||
void UpdatePagesCachedCount(PAddr addr, u32 size, int delta);
|
||||
|
||||
SurfaceCache surface_cache;
|
||||
OGLFramebuffer transfer_framebuffers[2];
|
||||
PageMap cached_pages;
|
||||
SurfaceMap dirty_regions;
|
||||
SurfaceSet remove_surfaces;
|
||||
|
||||
OGLFramebuffer read_framebuffer;
|
||||
OGLFramebuffer draw_framebuffer;
|
||||
|
||||
OGLVertexArray attributeless_vao;
|
||||
OGLBuffer d24s8_abgr_buffer;
|
||||
GLsizeiptr d24s8_abgr_buffer_size;
|
||||
OGLShader d24s8_abgr_shader;
|
||||
GLint d24s8_abgr_tbo_size_u_id;
|
||||
GLint d24s8_abgr_viewport_u_id;
|
||||
};
|
||||
|
|
|
@ -36,7 +36,7 @@ public:
|
|||
if (handle == 0)
|
||||
return;
|
||||
glDeleteTextures(1, &handle);
|
||||
OpenGLState::ResetTexture(handle);
|
||||
OpenGLState::GetCurState().ResetTexture(handle).Apply();
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
|
@ -69,7 +69,7 @@ public:
|
|||
if (handle == 0)
|
||||
return;
|
||||
glDeleteSamplers(1, &handle);
|
||||
OpenGLState::ResetSampler(handle);
|
||||
OpenGLState::GetCurState().ResetSampler(handle).Apply();
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
|
@ -102,7 +102,7 @@ public:
|
|||
if (handle == 0)
|
||||
return;
|
||||
glDeleteProgram(handle);
|
||||
OpenGLState::ResetProgram(handle);
|
||||
OpenGLState::GetCurState().ResetProgram(handle).Apply();
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
|
@ -135,7 +135,7 @@ public:
|
|||
if (handle == 0)
|
||||
return;
|
||||
glDeleteBuffers(1, &handle);
|
||||
OpenGLState::ResetBuffer(handle);
|
||||
OpenGLState::GetCurState().ResetBuffer(handle).Apply();
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
|
@ -168,7 +168,7 @@ public:
|
|||
if (handle == 0)
|
||||
return;
|
||||
glDeleteVertexArrays(1, &handle);
|
||||
OpenGLState::ResetVertexArray(handle);
|
||||
OpenGLState::GetCurState().ResetVertexArray(handle).Apply();
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
|
@ -201,7 +201,7 @@ public:
|
|||
if (handle == 0)
|
||||
return;
|
||||
glDeleteFramebuffers(1, &handle);
|
||||
OpenGLState::ResetFramebuffer(handle);
|
||||
OpenGLState::GetCurState().ResetFramebuffer(handle).Apply();
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ struct LightSrc {
|
|||
};
|
||||
|
||||
layout (std140) uniform shader_data {
|
||||
vec2 framebuffer_scale;
|
||||
int framebuffer_scale;
|
||||
int alphatest_ref;
|
||||
float depth_scale;
|
||||
float depth_offset;
|
||||
|
|
|
@ -69,6 +69,17 @@ OpenGLState::OpenGLState() {
|
|||
draw.uniform_buffer = 0;
|
||||
draw.shader_program = 0;
|
||||
|
||||
scissor.enabled = false;
|
||||
scissor.x = 0;
|
||||
scissor.y = 0;
|
||||
scissor.width = 0;
|
||||
scissor.height = 0;
|
||||
|
||||
viewport.x = 0;
|
||||
viewport.y = 0;
|
||||
viewport.width = 0;
|
||||
viewport.height = 0;
|
||||
|
||||
clip_distance = {};
|
||||
}
|
||||
|
||||
|
@ -193,7 +204,7 @@ void OpenGLState::Apply() const {
|
|||
// Lighting LUTs
|
||||
if (lighting_lut.texture_buffer != cur_state.lighting_lut.texture_buffer) {
|
||||
glActiveTexture(TextureUnits::LightingLUT.Enum());
|
||||
glBindTexture(GL_TEXTURE_BUFFER, cur_state.lighting_lut.texture_buffer);
|
||||
glBindTexture(GL_TEXTURE_BUFFER, lighting_lut.texture_buffer);
|
||||
}
|
||||
|
||||
// Fog LUT
|
||||
|
@ -260,6 +271,26 @@ void OpenGLState::Apply() const {
|
|||
glUseProgram(draw.shader_program);
|
||||
}
|
||||
|
||||
// Scissor test
|
||||
if (scissor.enabled != cur_state.scissor.enabled) {
|
||||
if (scissor.enabled) {
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
} else {
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
}
|
||||
}
|
||||
|
||||
if (scissor.x != cur_state.scissor.x || scissor.y != cur_state.scissor.y ||
|
||||
scissor.width != cur_state.scissor.width || scissor.height != cur_state.scissor.height) {
|
||||
glScissor(scissor.x, scissor.y, scissor.width, scissor.height);
|
||||
}
|
||||
|
||||
if (viewport.x != cur_state.viewport.x || viewport.y != cur_state.viewport.y ||
|
||||
viewport.width != cur_state.viewport.width ||
|
||||
viewport.height != cur_state.viewport.height) {
|
||||
glViewport(viewport.x, viewport.y, viewport.width, viewport.height);
|
||||
}
|
||||
|
||||
// Clip distance
|
||||
for (size_t i = 0; i < clip_distance.size(); ++i) {
|
||||
if (clip_distance[i] != cur_state.clip_distance[i]) {
|
||||
|
@ -274,62 +305,68 @@ void OpenGLState::Apply() const {
|
|||
cur_state = *this;
|
||||
}
|
||||
|
||||
void OpenGLState::ResetTexture(GLuint handle) {
|
||||
for (auto& unit : cur_state.texture_units) {
|
||||
OpenGLState& OpenGLState::ResetTexture(GLuint handle) {
|
||||
for (auto& unit : texture_units) {
|
||||
if (unit.texture_2d == handle) {
|
||||
unit.texture_2d = 0;
|
||||
}
|
||||
}
|
||||
if (cur_state.lighting_lut.texture_buffer == handle)
|
||||
cur_state.lighting_lut.texture_buffer = 0;
|
||||
if (cur_state.fog_lut.texture_buffer == handle)
|
||||
cur_state.fog_lut.texture_buffer = 0;
|
||||
if (cur_state.proctex_noise_lut.texture_buffer == handle)
|
||||
cur_state.proctex_noise_lut.texture_buffer = 0;
|
||||
if (cur_state.proctex_color_map.texture_buffer == handle)
|
||||
cur_state.proctex_color_map.texture_buffer = 0;
|
||||
if (cur_state.proctex_alpha_map.texture_buffer == handle)
|
||||
cur_state.proctex_alpha_map.texture_buffer = 0;
|
||||
if (cur_state.proctex_lut.texture_buffer == handle)
|
||||
cur_state.proctex_lut.texture_buffer = 0;
|
||||
if (cur_state.proctex_diff_lut.texture_buffer == handle)
|
||||
cur_state.proctex_diff_lut.texture_buffer = 0;
|
||||
if (lighting_lut.texture_buffer == handle)
|
||||
lighting_lut.texture_buffer = 0;
|
||||
if (fog_lut.texture_buffer == handle)
|
||||
fog_lut.texture_buffer = 0;
|
||||
if (proctex_noise_lut.texture_buffer == handle)
|
||||
proctex_noise_lut.texture_buffer = 0;
|
||||
if (proctex_color_map.texture_buffer == handle)
|
||||
proctex_color_map.texture_buffer = 0;
|
||||
if (proctex_alpha_map.texture_buffer == handle)
|
||||
proctex_alpha_map.texture_buffer = 0;
|
||||
if (proctex_lut.texture_buffer == handle)
|
||||
proctex_lut.texture_buffer = 0;
|
||||
if (proctex_diff_lut.texture_buffer == handle)
|
||||
proctex_diff_lut.texture_buffer = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void OpenGLState::ResetSampler(GLuint handle) {
|
||||
for (auto& unit : cur_state.texture_units) {
|
||||
OpenGLState& OpenGLState::ResetSampler(GLuint handle) {
|
||||
for (auto& unit : texture_units) {
|
||||
if (unit.sampler == handle) {
|
||||
unit.sampler = 0;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void OpenGLState::ResetProgram(GLuint handle) {
|
||||
if (cur_state.draw.shader_program == handle) {
|
||||
cur_state.draw.shader_program = 0;
|
||||
OpenGLState& OpenGLState::ResetProgram(GLuint handle) {
|
||||
if (draw.shader_program == handle) {
|
||||
draw.shader_program = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void OpenGLState::ResetBuffer(GLuint handle) {
|
||||
if (cur_state.draw.vertex_buffer == handle) {
|
||||
cur_state.draw.vertex_buffer = 0;
|
||||
OpenGLState& OpenGLState::ResetBuffer(GLuint handle) {
|
||||
if (draw.vertex_buffer == handle) {
|
||||
draw.vertex_buffer = 0;
|
||||
}
|
||||
if (cur_state.draw.uniform_buffer == handle) {
|
||||
cur_state.draw.uniform_buffer = 0;
|
||||
if (draw.uniform_buffer == handle) {
|
||||
draw.uniform_buffer = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void OpenGLState::ResetVertexArray(GLuint handle) {
|
||||
if (cur_state.draw.vertex_array == handle) {
|
||||
cur_state.draw.vertex_array = 0;
|
||||
OpenGLState& OpenGLState::ResetVertexArray(GLuint handle) {
|
||||
if (draw.vertex_array == handle) {
|
||||
draw.vertex_array = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void OpenGLState::ResetFramebuffer(GLuint handle) {
|
||||
if (cur_state.draw.read_framebuffer == handle) {
|
||||
cur_state.draw.read_framebuffer = 0;
|
||||
OpenGLState& OpenGLState::ResetFramebuffer(GLuint handle) {
|
||||
if (draw.read_framebuffer == handle) {
|
||||
draw.read_framebuffer = 0;
|
||||
}
|
||||
if (cur_state.draw.draw_framebuffer == handle) {
|
||||
cur_state.draw.draw_framebuffer = 0;
|
||||
if (draw.draw_framebuffer == handle) {
|
||||
draw.draw_framebuffer = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
|
|
@ -124,25 +124,40 @@ public:
|
|||
GLuint shader_program; // GL_CURRENT_PROGRAM
|
||||
} draw;
|
||||
|
||||
struct {
|
||||
bool enabled; // GL_SCISSOR_TEST
|
||||
GLint x;
|
||||
GLint y;
|
||||
GLsizei width;
|
||||
GLsizei height;
|
||||
} scissor;
|
||||
|
||||
struct {
|
||||
GLint x;
|
||||
GLint y;
|
||||
GLsizei width;
|
||||
GLsizei height;
|
||||
} viewport;
|
||||
|
||||
std::array<bool, 2> clip_distance; // GL_CLIP_DISTANCE
|
||||
|
||||
OpenGLState();
|
||||
|
||||
/// Get the currently active OpenGL state
|
||||
static const OpenGLState& GetCurState() {
|
||||
static OpenGLState GetCurState() {
|
||||
return cur_state;
|
||||
}
|
||||
|
||||
/// Apply this state as the current OpenGL state
|
||||
void Apply() const;
|
||||
|
||||
/// Resets and unbinds any references to the given resource in the current OpenGL state
|
||||
static void ResetTexture(GLuint handle);
|
||||
static void ResetSampler(GLuint handle);
|
||||
static void ResetProgram(GLuint handle);
|
||||
static void ResetBuffer(GLuint handle);
|
||||
static void ResetVertexArray(GLuint handle);
|
||||
static void ResetFramebuffer(GLuint handle);
|
||||
/// Resets any references to the given resource
|
||||
OpenGLState& ResetTexture(GLuint handle);
|
||||
OpenGLState& ResetSampler(GLuint handle);
|
||||
OpenGLState& ResetProgram(GLuint handle);
|
||||
OpenGLState& ResetBuffer(GLuint handle);
|
||||
OpenGLState& ResetVertexArray(GLuint handle);
|
||||
OpenGLState& ResetFramebuffer(GLuint handle);
|
||||
|
||||
private:
|
||||
static OpenGLState cur_state;
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace Pica {
|
|||
namespace Shader {
|
||||
struct OutputVertex;
|
||||
}
|
||||
}
|
||||
} // namespace Pica
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
|
@ -22,6 +22,7 @@ class SWRasterizer : public RasterizerInterface {
|
|||
void NotifyPicaRegisterChanged(u32 id) override {}
|
||||
void FlushAll() override {}
|
||||
void FlushRegion(PAddr addr, u32 size) override {}
|
||||
void InvalidateRegion(PAddr addr, u32 size) override {}
|
||||
void FlushAndInvalidateRegion(PAddr addr, u32 size) override {}
|
||||
};
|
||||
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
namespace VideoCore {
|
||||
|
||||
// 8x8 Z-Order coordinate from 2D coordinates
|
||||
static inline u32 MortonInterleave(u32 x, u32 y) {
|
||||
static const u32 xlut[] = {0x00, 0x01, 0x04, 0x05, 0x10, 0x11, 0x14, 0x15};
|
||||
static const u32 ylut[] = {0x00, 0x02, 0x08, 0x0a, 0x20, 0x22, 0x28, 0x2a};
|
||||
static constexpr u32 MortonInterleave(u32 x, u32 y) {
|
||||
constexpr u32 xlut[] = {0x00, 0x01, 0x04, 0x05, 0x10, 0x11, 0x14, 0x15};
|
||||
constexpr u32 ylut[] = {0x00, 0x02, 0x08, 0x0a, 0x20, 0x22, 0x28, 0x2a};
|
||||
return xlut[x % 8] + ylut[y % 8];
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue