mirror of
https://github.com/citra-emu/citra.git
synced 2024-11-26 21:30:05 +00:00
viewport fix
This commit is contained in:
parent
8969245e2e
commit
779185841c
@ -242,13 +242,37 @@ void RasterizerOpenGL::DrawTriangles() {
|
||||
const bool using_depth_fb = regs.framebuffer.framebuffer.GetDepthBufferPhysicalAddress() != 0 &&
|
||||
(write_depth_fb || state.depth.test_enabled || (has_stencil && state.stencil.test_enabled));
|
||||
|
||||
// Sync and bind the framebuffer surfaces
|
||||
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> rect;
|
||||
std::tie(color_surface, depth_surface, rect) =
|
||||
res_cache.GetFramebufferSurfaces(using_color_fb, using_depth_fb);
|
||||
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{
|
||||
MathUtil::Clamp(surfaces_rect.left + viewport_rect_unscaled.left * res_scale, // left
|
||||
surfaces_rect.left, surfaces_rect.right),
|
||||
MathUtil::Clamp(surfaces_rect.bottom + viewport_rect_unscaled.GetHeight() * res_scale, // top
|
||||
surfaces_rect.bottom, surfaces_rect.top),
|
||||
MathUtil::Clamp(surfaces_rect.left + viewport_rect_unscaled.GetWidth() * res_scale, // right
|
||||
surfaces_rect.left, surfaces_rect.right),
|
||||
MathUtil::Clamp(surfaces_rect.bottom + viewport_rect_unscaled.bottom * res_scale, // bottom
|
||||
surfaces_rect.bottom, surfaces_rect.top)
|
||||
};
|
||||
|
||||
// Bind the framebuffer surfaces
|
||||
state.draw.draw_framebuffer = framebuffer.handle;
|
||||
state.Apply();
|
||||
|
||||
@ -273,20 +297,11 @@ void RasterizerOpenGL::DrawTriangles() {
|
||||
}
|
||||
|
||||
// Sync the viewport
|
||||
// These registers hold half-width and half-height, so must be multiplied by 2
|
||||
const GLsizei viewport_width =
|
||||
static_cast<GLsizei>(Pica::float24::FromRaw(regs.rasterizer.viewport_size_x).ToFloat32() * 2);
|
||||
const GLsizei viewport_height =
|
||||
static_cast<GLsizei>(Pica::float24::FromRaw(regs.rasterizer.viewport_size_y).ToFloat32() * 2);
|
||||
|
||||
const u16 res_scale = color_surface != nullptr ? color_surface->res_scale :
|
||||
(depth_surface == nullptr ? 1u : depth_surface->res_scale);
|
||||
|
||||
glViewport(
|
||||
static_cast<GLint>(rect.left + regs.rasterizer.viewport_corner.x * res_scale),
|
||||
static_cast<GLint>(rect.bottom + regs.rasterizer.viewport_corner.y * res_scale),
|
||||
viewport_width * res_scale,
|
||||
viewport_height * res_scale);
|
||||
static_cast<GLint>(surfaces_rect.left + viewport_rect_unscaled.left * res_scale),
|
||||
static_cast<GLint>(surfaces_rect.bottom + viewport_rect_unscaled.bottom * res_scale),
|
||||
static_cast<GLsizei>(viewport_rect_unscaled.GetWidth() * res_scale),
|
||||
static_cast<GLsizei>(viewport_rect_unscaled.GetHeight() * res_scale));
|
||||
|
||||
if (uniform_block_data.data.framebuffer_scale != res_scale) {
|
||||
uniform_block_data.data.framebuffer_scale = res_scale;
|
||||
@ -296,15 +311,15 @@ void RasterizerOpenGL::DrawTriangles() {
|
||||
// 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 * res_scale);
|
||||
surfaces_rect.left + regs.rasterizer.scissor_test.x1 * res_scale);
|
||||
GLint scissor_y1 = static_cast<GLint>(
|
||||
rect.bottom + regs.rasterizer.scissor_test.y1 * res_scale);
|
||||
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) * res_scale);
|
||||
surfaces_rect.left + (regs.rasterizer.scissor_test.x2 + 1) * res_scale);
|
||||
GLint scissor_y2 = static_cast<GLint>(
|
||||
rect.bottom + (regs.rasterizer.scissor_test.y2 + 1) * res_scale);
|
||||
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 ||
|
||||
@ -394,6 +409,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
|
||||
@ -401,29 +425,8 @@ void RasterizerOpenGL::DrawTriangles() {
|
||||
GL_STREAM_DRAW);
|
||||
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)vertex_batch.size());
|
||||
|
||||
// Mark framebuffer surfaces as dirty
|
||||
const u32 viewport_offset =
|
||||
((regs.framebuffer.framebuffer.GetHeight() - regs.rasterizer.viewport_corner.y - viewport_height)
|
||||
* regs.framebuffer.framebuffer.GetWidth())
|
||||
+ regs.rasterizer.viewport_corner.x;
|
||||
|
||||
const u32 viewport_size = ((viewport_height - 1) * regs.framebuffer.framebuffer.GetWidth())
|
||||
+ viewport_width;
|
||||
|
||||
if (color_surface != nullptr && write_color_fb) {
|
||||
res_cache.InvalidateRegion(
|
||||
regs.framebuffer.framebuffer.GetColorBufferPhysicalAddress()
|
||||
+ (viewport_offset * color_surface->bytes_per_pixel),
|
||||
viewport_size * color_surface->bytes_per_pixel,
|
||||
color_surface);
|
||||
}
|
||||
if (depth_surface != nullptr && write_depth_fb) {
|
||||
res_cache.InvalidateRegion(
|
||||
regs.framebuffer.framebuffer.GetDepthBufferPhysicalAddress()
|
||||
+ (viewport_offset * depth_surface->bytes_per_pixel),
|
||||
viewport_size * depth_surface->bytes_per_pixel,
|
||||
depth_surface);
|
||||
}
|
||||
// Disable scissor test
|
||||
state.scissor.enabled = false;
|
||||
|
||||
vertex_batch.clear();
|
||||
|
||||
@ -432,6 +435,25 @@ 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) {
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <vector>
|
||||
#include <boost/range.hpp>
|
||||
#include <glad/glad.h>
|
||||
#include "common/alignment.h"
|
||||
#include "common/bit_field.h"
|
||||
#include "common/color.h"
|
||||
#include "common/logging/log.h"
|
||||
@ -312,7 +313,26 @@ static bool FillSurface(const Surface& surface, const u8* fill_data) {
|
||||
return true;
|
||||
}
|
||||
|
||||
MathUtil::Rectangle<u32> CachedSurface::GetSubRect(const SurfaceParams& sub_surface) const {
|
||||
SurfaceInterval SurfaceParams::GetSubRectInterval(MathUtil::Rectangle<u32> unscaled_rect) const {
|
||||
if (unscaled_rect.top > unscaled_rect.bottom) {
|
||||
std::swap(unscaled_rect.top, unscaled_rect.bottom);
|
||||
}
|
||||
if (is_tiled) {
|
||||
unscaled_rect.left = Common::AlignDown(unscaled_rect.left, 8);
|
||||
unscaled_rect.top = Common::AlignDown(unscaled_rect.top, 8);
|
||||
unscaled_rect.right = Common::AlignUp(unscaled_rect.right, 8);
|
||||
unscaled_rect.bottom = Common::AlignUp(unscaled_rect.bottom, 8);
|
||||
}
|
||||
|
||||
const u32 pixel_offset = unscaled_rect.left + stride *
|
||||
(!is_tiled ? unscaled_rect.top : height - unscaled_rect.top - unscaled_rect.GetHeight());
|
||||
|
||||
const u32 pixels = (unscaled_rect.GetHeight() - 1) * stride + unscaled_rect.GetWidth();
|
||||
|
||||
return { addr + BytesInPixels(pixel_offset), addr + BytesInPixels(pixel_offset + pixels) };
|
||||
}
|
||||
|
||||
MathUtil::Rectangle<u32> SurfaceParams::GetSubRect(const SurfaceParams& sub_surface) const {
|
||||
const u32 begin_pixel_index = PixelsInBytes(sub_surface.addr - addr);
|
||||
const int x0 = begin_pixel_index % stride;
|
||||
const int y0 = begin_pixel_index / stride;
|
||||
@ -323,7 +343,7 @@ MathUtil::Rectangle<u32> CachedSurface::GetSubRect(const SurfaceParams& sub_surf
|
||||
return MathUtil::Rectangle<u32>(x0, y0, x0 + sub_surface.width, y0 + sub_surface.height); // Top to bottom
|
||||
}
|
||||
|
||||
MathUtil::Rectangle<u32> CachedSurface::GetScaledSubRect(const SurfaceParams& sub_surface) const {
|
||||
MathUtil::Rectangle<u32> SurfaceParams::GetScaledSubRect(const SurfaceParams& sub_surface) const {
|
||||
auto rect = GetSubRect(sub_surface);
|
||||
rect.left = rect.left * res_scale;
|
||||
rect.right = rect.right * res_scale;
|
||||
@ -332,7 +352,7 @@ MathUtil::Rectangle<u32> CachedSurface::GetScaledSubRect(const SurfaceParams& su
|
||||
return rect;
|
||||
}
|
||||
|
||||
bool CachedSurface::ExactMatch(const SurfaceParams& other_surface) const {
|
||||
bool SurfaceParams::ExactMatch(const SurfaceParams& other_surface) const {
|
||||
return (other_surface.addr == addr &&
|
||||
other_surface.width == width &&
|
||||
other_surface.height == height &&
|
||||
@ -341,7 +361,7 @@ bool CachedSurface::ExactMatch(const SurfaceParams& other_surface) const {
|
||||
other_surface.is_tiled == is_tiled);
|
||||
}
|
||||
|
||||
bool CachedSurface::CanSubRect(const SurfaceParams& sub_surface) const {
|
||||
bool SurfaceParams::CanSubRect(const SurfaceParams& sub_surface) const {
|
||||
if (sub_surface.addr < addr || sub_surface.end > end || sub_surface.stride != stride ||
|
||||
sub_surface.pixel_format != pixel_format || sub_surface.is_tiled != is_tiled)
|
||||
return false;
|
||||
@ -357,36 +377,7 @@ bool CachedSurface::CanSubRect(const SurfaceParams& sub_surface) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CachedSurface::CanCopy(const SurfaceParams& dest_surface) const {
|
||||
if (type == SurfaceType::Fill && IsRegionValid(dest_surface.GetInterval()) &&
|
||||
dest_surface.addr >= addr && dest_surface.end <= end) { // dest_surface is within our fill range
|
||||
if (fill_size != dest_surface.bytes_per_pixel) {
|
||||
if (dest_surface.is_tiled && GetFormatBpp(dest_surface.pixel_format) * 8 % fill_size != 0)
|
||||
return false;
|
||||
|
||||
// Check if bits repeat for our fill_size
|
||||
const u32 dest_bytes_per_pixel = std::max(dest_surface.bytes_per_pixel, 1u); // Take care of 4bpp formats
|
||||
std::vector<u8> fill_test(fill_size * dest_bytes_per_pixel);
|
||||
|
||||
for (u32 i = 0; i < dest_bytes_per_pixel; ++i)
|
||||
std::memcpy(&fill_test[i * fill_size], &fill_data[0], fill_size);
|
||||
|
||||
for (u32 i = 0; i < fill_size; ++i)
|
||||
if (std::memcmp(&fill_test[dest_bytes_per_pixel * i], &fill_test[0], dest_bytes_per_pixel) != 0)
|
||||
return false;
|
||||
|
||||
if (dest_surface.bytes_per_pixel == 0 && (fill_test[0] & 0xF) != (fill_test[0] >> 4)) // 4bpp compare
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (CanSubRect(dest_surface) && dest_surface.width == stride)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CachedSurface::CanExpand(const SurfaceParams& expanded_surface) const {
|
||||
bool SurfaceParams::CanExpand(const SurfaceParams& expanded_surface) const {
|
||||
if (pixel_format != expanded_surface.pixel_format ||
|
||||
is_tiled != expanded_surface.is_tiled ||
|
||||
addr > expanded_surface.end || expanded_surface.addr > end ||
|
||||
@ -402,7 +393,7 @@ bool CachedSurface::CanExpand(const SurfaceParams& expanded_surface) const {
|
||||
return x0 == 0 && (!is_tiled || y0 % 8 == 0);
|
||||
}
|
||||
|
||||
bool CachedSurface::CanTexCopy(const SurfaceParams& texcopy_params) const {
|
||||
bool SurfaceParams::CanTexCopy(const SurfaceParams& texcopy_params) const {
|
||||
// TODO: Accept "Fill" surfaces
|
||||
if (pixel_format == PixelFormat::Invalid ||
|
||||
addr > texcopy_params.addr || end < texcopy_params.end ||
|
||||
@ -425,6 +416,35 @@ bool CachedSurface::CanTexCopy(const SurfaceParams& texcopy_params) const {
|
||||
x0 + PixelsInBytes(texcopy_params.width / 8) <= stride);
|
||||
}
|
||||
|
||||
bool CachedSurface::CanCopy(const SurfaceParams& dest_surface) const {
|
||||
if (type == SurfaceType::Fill && IsRegionValid(dest_surface.GetInterval()) &&
|
||||
dest_surface.addr >= addr && dest_surface.end <= end) { // dest_surface is within our fill range
|
||||
if (fill_size != dest_surface.BytesPerPixel()) {
|
||||
if (dest_surface.is_tiled && BytesInPixels(8 * 8) % fill_size != 0)
|
||||
return false;
|
||||
|
||||
// Check if bits repeat for our fill_size
|
||||
const u32 dest_bytes_per_pixel = std::max(dest_surface.BytesPerPixel(), 1u); // Take care of 4bpp formats
|
||||
std::vector<u8> fill_test(fill_size * dest_bytes_per_pixel);
|
||||
|
||||
for (u32 i = 0; i < dest_bytes_per_pixel; ++i)
|
||||
std::memcpy(&fill_test[i * fill_size], &fill_data[0], fill_size);
|
||||
|
||||
for (u32 i = 0; i < fill_size; ++i)
|
||||
if (std::memcmp(&fill_test[dest_bytes_per_pixel * i], &fill_test[0], dest_bytes_per_pixel) != 0)
|
||||
return false;
|
||||
|
||||
if (dest_surface.BytesPerPixel() == 0 && (fill_test[0] & 0xF) != (fill_test[0] >> 4)) // 4bpp compare
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (CanSubRect(dest_surface) && dest_surface.width == stride)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void CopySurface(const Surface& src_surface, const Surface& dest_surface) {
|
||||
if (src_surface == dest_surface)
|
||||
return;
|
||||
@ -492,7 +512,7 @@ void CachedSurface::LoadGLBuffer(PAddr load_start, PAddr load_end) {
|
||||
}
|
||||
else {
|
||||
size_t copyfn_offset = MortonCopyFlags::MortonToGl;
|
||||
copyfn_offset |= (bytes_per_pixel - 1) << MortonCopyFlags::BytesPerPixelBits;
|
||||
copyfn_offset |= (BytesPerPixel() - 1) << MortonCopyFlags::BytesPerPixelBits;
|
||||
copyfn_offset |= (gl_bytes_per_pixel - 1) << MortonCopyFlags::GLBytesPerPixelBits;
|
||||
|
||||
if (load_start != addr || load_end != end)
|
||||
@ -544,7 +564,7 @@ void CachedSurface::FlushGLBuffer(PAddr flush_start, PAddr flush_end) {
|
||||
std::memcpy(dst_buffer + start_offset, &gl_buffer[start_offset], flush_end - flush_start);
|
||||
}
|
||||
else {
|
||||
size_t copyfn_offset = (bytes_per_pixel - 1) << MortonCopyFlags::BytesPerPixelBits;
|
||||
size_t copyfn_offset = (BytesPerPixel() - 1) << MortonCopyFlags::BytesPerPixelBits;
|
||||
copyfn_offset |= (gl_bytes_per_pixel - 1) << MortonCopyFlags::GLBytesPerPixelBits;
|
||||
|
||||
if (flush_start != addr || flush_end != end)
|
||||
@ -806,7 +826,7 @@ SurfaceRect_Tuple RasterizerCacheOpenGL::GetSurfaceSubRect(const SurfaceParams&
|
||||
new_params.addr = std::min(params.addr, surface->addr);
|
||||
new_params.end = std::max(params.end, surface->end);
|
||||
new_params.size = new_params.end - new_params.addr;
|
||||
new_params.height = new_params.size / (SurfaceParams::GetFormatBpp(params.pixel_format) * params.stride / 8);
|
||||
new_params.height = new_params.size / params.BytesInPixels(params.stride);
|
||||
|
||||
Surface new_surface = CreateSurface(new_params);
|
||||
RegisterSurface(new_surface);
|
||||
@ -861,30 +881,12 @@ constexpr u16 GetResolutionScaleFactor() {
|
||||
Settings::values.resolution_factor;
|
||||
}
|
||||
|
||||
SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces(bool using_color_fb,
|
||||
bool using_depth_fb) {
|
||||
SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces(
|
||||
bool using_color_fb, bool using_depth_fb,
|
||||
const MathUtil::Rectangle<s32>& viewport_rect) {
|
||||
const auto& regs = Pica::g_state.regs;
|
||||
const auto& config = regs.framebuffer.framebuffer;
|
||||
|
||||
// Make sur that framebuffers don't overlap if both color and depth are being used
|
||||
u32 fb_area = config.GetWidth() * config.GetHeight();
|
||||
bool framebuffers_overlap = using_color_fb && using_depth_fb &&
|
||||
MathUtil::IntervalsIntersect(
|
||||
config.GetColorBufferPhysicalAddress(),
|
||||
fb_area * GPU::Regs::BytesPerPixel(GPU::Regs::PixelFormat(config.color_format.Value())),
|
||||
config.GetDepthBufferPhysicalAddress(),
|
||||
fb_area * Pica::FramebufferRegs::BytesPerDepthPixel(config.depth_format));
|
||||
|
||||
if (framebuffers_overlap) {
|
||||
LOG_CRITICAL(Render_OpenGL, "Color and depth framebuffer memory regions overlap; overlapping framebuffers not supported!");
|
||||
using_depth_fb = false;
|
||||
}
|
||||
|
||||
// get color and depth surfaces
|
||||
SurfaceParams color_params;
|
||||
SurfaceParams depth_params;
|
||||
color_params.is_tiled = depth_params.is_tiled = true;
|
||||
|
||||
// update resolution_scale_factor and reset cache if changed
|
||||
static u16 resolution_scale_factor = GetResolutionScaleFactor();
|
||||
if (resolution_scale_factor != GetResolutionScaleFactor()) {
|
||||
@ -893,38 +895,63 @@ SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces(bool usin
|
||||
InvalidateRegion(0, 0xffffffff, nullptr);
|
||||
}
|
||||
|
||||
color_params.addr = config.GetColorBufferPhysicalAddress();
|
||||
color_params.width = depth_params.width = config.GetWidth();
|
||||
color_params.height = depth_params.height = config.GetHeight();
|
||||
color_params.pixel_format = SurfaceParams::PixelFormatFromColorFormat(config.color_format);
|
||||
MathUtil::Rectangle<u32> viewport_clamped{
|
||||
static_cast<u32>(MathUtil::Clamp(viewport_rect.left, 0, static_cast<s32>(config.GetWidth()))),
|
||||
static_cast<u32>(MathUtil::Clamp(viewport_rect.top, 0, static_cast<s32>(config.GetHeight()))),
|
||||
static_cast<u32>(MathUtil::Clamp(viewport_rect.right, 0, static_cast<s32>(config.GetWidth()))),
|
||||
static_cast<u32>(MathUtil::Clamp(viewport_rect.bottom, 0, static_cast<s32>(config.GetHeight())))
|
||||
};
|
||||
|
||||
// get color and depth surfaces
|
||||
SurfaceParams color_params;
|
||||
color_params.is_tiled = true;
|
||||
color_params.res_scale = resolution_scale_factor;
|
||||
color_params.width = config.GetWidth();
|
||||
color_params.height = config.GetHeight();
|
||||
SurfaceParams depth_params = color_params;
|
||||
|
||||
color_params.addr = config.GetColorBufferPhysicalAddress();
|
||||
color_params.pixel_format = SurfaceParams::PixelFormatFromColorFormat(config.color_format);
|
||||
color_params.UpdateParams();
|
||||
|
||||
depth_params.addr = config.GetDepthBufferPhysicalAddress();
|
||||
depth_params.pixel_format = SurfaceParams::PixelFormatFromDepthFormat(config.depth_format);
|
||||
depth_params.UpdateParams();
|
||||
|
||||
auto color_vp_interval = color_params.GetSubRectInterval(viewport_clamped);
|
||||
auto depth_vp_interval = depth_params.GetSubRectInterval(viewport_clamped);
|
||||
|
||||
// Make sur that framebuffers don't overlap if both color and depth are being used
|
||||
if (using_color_fb && using_depth_fb &&
|
||||
boost::icl::length(color_vp_interval & depth_vp_interval)) {
|
||||
LOG_CRITICAL(Render_OpenGL, "Color and depth framebuffer memory regions overlap; overlapping framebuffers not supported!");
|
||||
using_depth_fb = false;
|
||||
}
|
||||
|
||||
MathUtil::Rectangle<u32> rect{};
|
||||
Surface color_surface = nullptr;
|
||||
if (using_color_fb)
|
||||
std::tie(color_surface, rect) = GetSurfaceSubRect(color_params, ScaleMatch::Exact, true);
|
||||
|
||||
depth_params.pixel_format = SurfaceParams::PixelFormatFromDepthFormat(config.depth_format);
|
||||
depth_params.addr = config.GetDepthBufferPhysicalAddress();
|
||||
depth_params.res_scale = resolution_scale_factor;
|
||||
depth_params.UpdateParams();
|
||||
|
||||
Surface depth_surface = nullptr;
|
||||
if (using_depth_fb && color_surface != nullptr) {
|
||||
const PAddr validate_addr = depth_params.addr;
|
||||
const u32 validate_size = depth_params.size;
|
||||
if (using_color_fb)
|
||||
std::tie(color_surface, rect) = GetSurfaceSubRect(color_params, ScaleMatch::Exact, false);
|
||||
|
||||
if (using_depth_fb && color_surface != nullptr) {
|
||||
// Can't specify separate color and depth viewport offsets in OpenGL, so make sure depth_surface will have the same offsets
|
||||
depth_params.addr -= color_surface->PixelsInBytes(color_params.addr - color_surface->addr) * depth_params.bytes_per_pixel;
|
||||
depth_params.addr -= depth_params.BytesInPixels(
|
||||
color_surface->PixelsInBytes(color_params.addr - color_surface->addr));
|
||||
depth_params.height = color_surface->height;
|
||||
depth_params.UpdateParams();
|
||||
|
||||
depth_surface = GetSurface(depth_params, ScaleMatch::Exact, false);
|
||||
ValidateSurface(depth_surface, validate_addr, validate_size);
|
||||
}
|
||||
else if (using_depth_fb) {
|
||||
std::tie(depth_surface, rect) = GetSurfaceSubRect(depth_params, ScaleMatch::Exact, true);
|
||||
std::tie(depth_surface, rect) = GetSurfaceSubRect(depth_params, ScaleMatch::Exact, false);
|
||||
}
|
||||
|
||||
if (color_surface != nullptr) {
|
||||
ValidateSurface(color_surface, boost::icl::first(color_vp_interval), boost::icl::length(color_vp_interval));
|
||||
}
|
||||
if (depth_surface != nullptr) {
|
||||
ValidateSurface(depth_surface, boost::icl::first(depth_vp_interval), boost::icl::length(depth_vp_interval));
|
||||
}
|
||||
|
||||
return { color_surface, depth_surface, rect };
|
||||
@ -1003,14 +1030,14 @@ void RasterizerCacheOpenGL::ValidateSurface(const Surface& surface, PAddr addr,
|
||||
SurfaceParams params = *surface;
|
||||
const u32 pixel_offset = params.PixelsInBytes(interval_start - params.addr);
|
||||
if (!params.is_tiled) {
|
||||
params.addr += (pixel_offset - (pixel_offset % params.width)) *
|
||||
SurfaceParams::GetFormatBpp(params.pixel_format) / 8; // Start of the row
|
||||
params.height = (params.PixelsInBytes(interval_end - params.addr - 1) / params.width) + 1;
|
||||
// Start of the row
|
||||
params.addr += params.BytesInPixels(pixel_offset - (pixel_offset % params.stride));
|
||||
params.height = (params.PixelsInBytes(interval_end - params.addr - 1) / params.stride) + 1;
|
||||
}
|
||||
else {
|
||||
params.addr += (pixel_offset - (pixel_offset % (params.width * 8))) *
|
||||
SurfaceParams::GetFormatBpp(params.pixel_format) / 8; // Start of the tiled row
|
||||
params.height = ((params.PixelsInBytes(interval_end - params.addr - 1) / (params.width * 8)) + 1) * 8;
|
||||
// Start of the tiled row
|
||||
params.addr += params.BytesInPixels(pixel_offset - (pixel_offset % (params.stride * 8)));
|
||||
params.height = ((params.PixelsInBytes(interval_end - params.addr - 1) / (params.stride * 8)) + 1) * 8;
|
||||
}
|
||||
params.UpdateParams();
|
||||
|
||||
@ -1147,7 +1174,7 @@ Surface RasterizerCacheOpenGL::CreateSurface(const SurfaceParams& params) {
|
||||
surface->gl_bytes_per_pixel =
|
||||
(surface->pixel_format == PixelFormat::D24 || surface->type == SurfaceType::Texture) ?
|
||||
4 :
|
||||
surface->bytes_per_pixel;
|
||||
surface->BytesPerPixel();
|
||||
|
||||
surface->gl_buffer_offset = (surface->pixel_format == PixelFormat::D24) ? 1 : 0;
|
||||
|
||||
|
@ -179,7 +179,7 @@ struct SurfaceParams {
|
||||
return SurfaceType::Invalid;
|
||||
}
|
||||
|
||||
/// Update the params "size", "end", "bytes_per_pixel" and "type" from the already set "addr", "width", "height" and "pixel_format"
|
||||
/// Update the params "size", "end" and "type" from the already set "addr", "width", "height" and "pixel_format"
|
||||
void UpdateParams() {
|
||||
size = width * height * GetFormatBpp(pixel_format) / 8;
|
||||
|
||||
@ -190,13 +190,14 @@ struct SurfaceParams {
|
||||
|
||||
end = addr + size;
|
||||
type = GetFormatType(pixel_format);
|
||||
bytes_per_pixel = GetFormatBpp(pixel_format) / 8;
|
||||
}
|
||||
|
||||
SurfaceInterval GetInterval() const {
|
||||
return SurfaceInterval::right_open(addr, end);
|
||||
}
|
||||
|
||||
SurfaceInterval GetSubRectInterval(MathUtil::Rectangle<u32> unscaled_rect) const;
|
||||
|
||||
u32 GetScaledWidth() const {
|
||||
return width * res_scale;
|
||||
}
|
||||
@ -217,6 +218,22 @@ struct SurfaceParams {
|
||||
return size * 8 / GetFormatBpp(pixel_format);
|
||||
}
|
||||
|
||||
u32 BytesInPixels(u32 pixels) const {
|
||||
return pixels * GetFormatBpp(pixel_format) / 8;
|
||||
}
|
||||
|
||||
u32 BytesPerPixel() const {
|
||||
return BytesInPixels(1);
|
||||
}
|
||||
|
||||
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;
|
||||
@ -227,20 +244,12 @@ struct SurfaceParams {
|
||||
u16 res_scale = 1;
|
||||
|
||||
bool is_tiled = false;
|
||||
u32 bytes_per_pixel = 0;
|
||||
PixelFormat pixel_format = PixelFormat::Invalid;
|
||||
SurfaceType type = SurfaceType::Invalid;
|
||||
};
|
||||
|
||||
struct CachedSurface : SurfaceParams {
|
||||
bool ExactMatch(const SurfaceParams& other_surface) const;
|
||||
bool CanSubRect(const SurfaceParams& sub_surface) const;
|
||||
bool CanCopy(const SurfaceParams& dest_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;
|
||||
|
||||
bool IsRegionValid(const SurfaceInterval& interval) const {
|
||||
return (invalid_regions.find(interval) == invalid_regions.end());
|
||||
@ -295,7 +304,8 @@ public:
|
||||
Surface GetTextureSurface(const Pica::TexturingRegs::FullTextureConfig& config);
|
||||
|
||||
/// Get the color and depth surfaces based on the framebuffer configuration
|
||||
SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb);
|
||||
SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb,
|
||||
const MathUtil::Rectangle<s32>& viewport_rect);
|
||||
|
||||
/// Get a surface that matches the fill config
|
||||
Surface GetFillSurface(const GPU::Regs::MemoryFillConfig& config);
|
||||
|
@ -69,6 +69,12 @@ 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;
|
||||
|
||||
clip_distance = {};
|
||||
}
|
||||
|
||||
@ -263,6 +269,22 @@ 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);
|
||||
}
|
||||
|
||||
// Clip distance
|
||||
for (size_t i = 0; i < clip_distance.size(); ++i) {
|
||||
if (clip_distance[i] != cur_state.clip_distance[i]) {
|
||||
|
@ -124,6 +124,14 @@ public:
|
||||
GLuint shader_program; // GL_CURRENT_PROGRAM
|
||||
} draw;
|
||||
|
||||
struct {
|
||||
bool enabled; // GL_SCISSOR_TEST
|
||||
GLint x;
|
||||
GLint y;
|
||||
GLsizei width;
|
||||
GLsizei height;
|
||||
} scissor;
|
||||
|
||||
std::array<bool, 2> clip_distance; // GL_CLIP_DISTANCE
|
||||
|
||||
OpenGLState();
|
||||
|
Loading…
Reference in New Issue
Block a user