viewport fix

This commit is contained in:
Phantom 2017-10-10 03:28:05 +02:00
parent 8969245e2e
commit 779185841c
5 changed files with 228 additions and 139 deletions

View File

@ -242,13 +242,37 @@ void RasterizerOpenGL::DrawTriangles() {
const bool using_depth_fb = regs.framebuffer.framebuffer.GetDepthBufferPhysicalAddress() != 0 && const bool using_depth_fb = regs.framebuffer.framebuffer.GetDepthBufferPhysicalAddress() != 0 &&
(write_depth_fb || state.depth.test_enabled || (has_stencil && state.stencil.test_enabled)); (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 color_surface;
Surface depth_surface; Surface depth_surface;
MathUtil::Rectangle<u32> rect; MathUtil::Rectangle<u32> surfaces_rect;
std::tie(color_surface, depth_surface, rect) = std::tie(color_surface, depth_surface, surfaces_rect) =
res_cache.GetFramebufferSurfaces(using_color_fb, using_depth_fb); 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.draw.draw_framebuffer = framebuffer.handle;
state.Apply(); state.Apply();
@ -273,20 +297,11 @@ void RasterizerOpenGL::DrawTriangles() {
} }
// Sync the viewport // 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( glViewport(
static_cast<GLint>(rect.left + regs.rasterizer.viewport_corner.x * res_scale), static_cast<GLint>(surfaces_rect.left + viewport_rect_unscaled.left * res_scale),
static_cast<GLint>(rect.bottom + regs.rasterizer.viewport_corner.y * res_scale), static_cast<GLint>(surfaces_rect.bottom + viewport_rect_unscaled.bottom * res_scale),
viewport_width * res_scale, static_cast<GLsizei>(viewport_rect_unscaled.GetWidth() * res_scale),
viewport_height * res_scale); static_cast<GLsizei>(viewport_rect_unscaled.GetHeight() * res_scale));
if (uniform_block_data.data.framebuffer_scale != res_scale) { if (uniform_block_data.data.framebuffer_scale != res_scale) {
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 // 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. // sub-rect changes, the scissor bounds also need to be updated.
GLint scissor_x1 = static_cast<GLint>( 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>( 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 // x2, y2 have +1 added to cover the entire pixel area, otherwise you might get cracks when
// scaling or doing multisampling. // scaling or doing multisampling.
GLint scissor_x2 = static_cast<GLint>( 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>( 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 || if (uniform_block_data.data.scissor_x1 != scissor_x1 ||
uniform_block_data.data.scissor_x2 != scissor_x2 || uniform_block_data.data.scissor_x2 != scissor_x2 ||
@ -394,6 +409,15 @@ void RasterizerOpenGL::DrawTriangles() {
uniform_block_data.dirty = false; 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(); state.Apply();
// Draw the vertex batch // Draw the vertex batch
@ -401,29 +425,8 @@ void RasterizerOpenGL::DrawTriangles() {
GL_STREAM_DRAW); GL_STREAM_DRAW);
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)vertex_batch.size()); glDrawArrays(GL_TRIANGLES, 0, (GLsizei)vertex_batch.size());
// Mark framebuffer surfaces as dirty // Disable scissor test
const u32 viewport_offset = state.scissor.enabled = false;
((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);
}
vertex_batch.clear(); vertex_batch.clear();
@ -432,6 +435,25 @@ void RasterizerOpenGL::DrawTriangles() {
state.texture_units[texture_index].texture_2d = 0; state.texture_units[texture_index].texture_2d = 0;
} }
state.Apply(); 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) { void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {

View File

@ -11,6 +11,7 @@
#include <vector> #include <vector>
#include <boost/range.hpp> #include <boost/range.hpp>
#include <glad/glad.h> #include <glad/glad.h>
#include "common/alignment.h"
#include "common/bit_field.h" #include "common/bit_field.h"
#include "common/color.h" #include "common/color.h"
#include "common/logging/log.h" #include "common/logging/log.h"
@ -312,7 +313,26 @@ static bool FillSurface(const Surface& surface, const u8* fill_data) {
return true; 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 u32 begin_pixel_index = PixelsInBytes(sub_surface.addr - addr);
const int x0 = begin_pixel_index % stride; const int x0 = begin_pixel_index % stride;
const int y0 = 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 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); auto rect = GetSubRect(sub_surface);
rect.left = rect.left * res_scale; rect.left = rect.left * res_scale;
rect.right = rect.right * res_scale; rect.right = rect.right * res_scale;
@ -332,7 +352,7 @@ MathUtil::Rectangle<u32> CachedSurface::GetScaledSubRect(const SurfaceParams& su
return rect; return rect;
} }
bool CachedSurface::ExactMatch(const SurfaceParams& other_surface) const { bool SurfaceParams::ExactMatch(const SurfaceParams& other_surface) const {
return (other_surface.addr == addr && return (other_surface.addr == addr &&
other_surface.width == width && other_surface.width == width &&
other_surface.height == height && other_surface.height == height &&
@ -341,7 +361,7 @@ bool CachedSurface::ExactMatch(const SurfaceParams& other_surface) const {
other_surface.is_tiled == is_tiled); 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 || 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) sub_surface.pixel_format != pixel_format || sub_surface.is_tiled != is_tiled)
return false; return false;
@ -357,36 +377,7 @@ bool CachedSurface::CanSubRect(const SurfaceParams& sub_surface) const {
return true; return true;
} }
bool CachedSurface::CanCopy(const SurfaceParams& dest_surface) const { bool SurfaceParams::CanExpand(const SurfaceParams& expanded_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 {
if (pixel_format != expanded_surface.pixel_format || if (pixel_format != expanded_surface.pixel_format ||
is_tiled != expanded_surface.is_tiled || is_tiled != expanded_surface.is_tiled ||
addr > expanded_surface.end || expanded_surface.addr > end || 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); 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 // TODO: Accept "Fill" surfaces
if (pixel_format == PixelFormat::Invalid || if (pixel_format == PixelFormat::Invalid ||
addr > texcopy_params.addr || end < texcopy_params.end || 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); 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) { static void CopySurface(const Surface& src_surface, const Surface& dest_surface) {
if (src_surface == dest_surface) if (src_surface == dest_surface)
return; return;
@ -492,7 +512,7 @@ void CachedSurface::LoadGLBuffer(PAddr load_start, PAddr load_end) {
} }
else { else {
size_t copyfn_offset = MortonCopyFlags::MortonToGl; 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; copyfn_offset |= (gl_bytes_per_pixel - 1) << MortonCopyFlags::GLBytesPerPixelBits;
if (load_start != addr || load_end != end) 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); std::memcpy(dst_buffer + start_offset, &gl_buffer[start_offset], flush_end - flush_start);
} }
else { 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; copyfn_offset |= (gl_bytes_per_pixel - 1) << MortonCopyFlags::GLBytesPerPixelBits;
if (flush_start != addr || flush_end != end) 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.addr = std::min(params.addr, surface->addr);
new_params.end = std::max(params.end, surface->end); new_params.end = std::max(params.end, surface->end);
new_params.size = new_params.end - new_params.addr; 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); Surface new_surface = CreateSurface(new_params);
RegisterSurface(new_surface); RegisterSurface(new_surface);
@ -861,30 +881,12 @@ constexpr u16 GetResolutionScaleFactor() {
Settings::values.resolution_factor; Settings::values.resolution_factor;
} }
SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces(bool using_color_fb, SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces(
bool using_depth_fb) { bool using_color_fb, bool using_depth_fb,
const MathUtil::Rectangle<s32>& viewport_rect) {
const auto& regs = Pica::g_state.regs; const auto& regs = Pica::g_state.regs;
const auto& config = regs.framebuffer.framebuffer; 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 // update resolution_scale_factor and reset cache if changed
static u16 resolution_scale_factor = GetResolutionScaleFactor(); static u16 resolution_scale_factor = GetResolutionScaleFactor();
if (resolution_scale_factor != GetResolutionScaleFactor()) { if (resolution_scale_factor != GetResolutionScaleFactor()) {
@ -893,38 +895,63 @@ SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces(bool usin
InvalidateRegion(0, 0xffffffff, nullptr); InvalidateRegion(0, 0xffffffff, nullptr);
} }
color_params.addr = config.GetColorBufferPhysicalAddress(); MathUtil::Rectangle<u32> viewport_clamped{
color_params.width = depth_params.width = config.GetWidth(); static_cast<u32>(MathUtil::Clamp(viewport_rect.left, 0, static_cast<s32>(config.GetWidth()))),
color_params.height = depth_params.height = config.GetHeight(); static_cast<u32>(MathUtil::Clamp(viewport_rect.top, 0, static_cast<s32>(config.GetHeight()))),
color_params.pixel_format = SurfaceParams::PixelFormatFromColorFormat(config.color_format); 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.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(); 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{}; MathUtil::Rectangle<u32> rect{};
Surface color_surface = nullptr; 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; Surface depth_surface = nullptr;
if (using_depth_fb && color_surface != nullptr) { if (using_color_fb)
const PAddr validate_addr = depth_params.addr; std::tie(color_surface, rect) = GetSurfaceSubRect(color_params, ScaleMatch::Exact, false);
const u32 validate_size = depth_params.size;
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 // 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.height = color_surface->height;
depth_params.UpdateParams(); depth_params.UpdateParams();
depth_surface = GetSurface(depth_params, ScaleMatch::Exact, false); depth_surface = GetSurface(depth_params, ScaleMatch::Exact, false);
ValidateSurface(depth_surface, validate_addr, validate_size);
} }
else if (using_depth_fb) { 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 }; return { color_surface, depth_surface, rect };
@ -1003,14 +1030,14 @@ void RasterizerCacheOpenGL::ValidateSurface(const Surface& surface, PAddr addr,
SurfaceParams params = *surface; SurfaceParams params = *surface;
const u32 pixel_offset = params.PixelsInBytes(interval_start - params.addr); const u32 pixel_offset = params.PixelsInBytes(interval_start - params.addr);
if (!params.is_tiled) { if (!params.is_tiled) {
params.addr += (pixel_offset - (pixel_offset % params.width)) * // Start of the row
SurfaceParams::GetFormatBpp(params.pixel_format) / 8; // Start of the row params.addr += params.BytesInPixels(pixel_offset - (pixel_offset % params.stride));
params.height = (params.PixelsInBytes(interval_end - params.addr - 1) / params.width) + 1; params.height = (params.PixelsInBytes(interval_end - params.addr - 1) / params.stride) + 1;
} }
else { else {
params.addr += (pixel_offset - (pixel_offset % (params.width * 8))) * // Start of the tiled row
SurfaceParams::GetFormatBpp(params.pixel_format) / 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.width * 8)) + 1) * 8; params.height = ((params.PixelsInBytes(interval_end - params.addr - 1) / (params.stride * 8)) + 1) * 8;
} }
params.UpdateParams(); params.UpdateParams();
@ -1147,7 +1174,7 @@ Surface RasterizerCacheOpenGL::CreateSurface(const SurfaceParams& params) {
surface->gl_bytes_per_pixel = surface->gl_bytes_per_pixel =
(surface->pixel_format == PixelFormat::D24 || surface->type == SurfaceType::Texture) ? (surface->pixel_format == PixelFormat::D24 || surface->type == SurfaceType::Texture) ?
4 : 4 :
surface->bytes_per_pixel; surface->BytesPerPixel();
surface->gl_buffer_offset = (surface->pixel_format == PixelFormat::D24) ? 1 : 0; surface->gl_buffer_offset = (surface->pixel_format == PixelFormat::D24) ? 1 : 0;

View File

@ -179,7 +179,7 @@ struct SurfaceParams {
return SurfaceType::Invalid; 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() { void UpdateParams() {
size = width * height * GetFormatBpp(pixel_format) / 8; size = width * height * GetFormatBpp(pixel_format) / 8;
@ -190,13 +190,14 @@ struct SurfaceParams {
end = addr + size; end = addr + size;
type = GetFormatType(pixel_format); type = GetFormatType(pixel_format);
bytes_per_pixel = GetFormatBpp(pixel_format) / 8;
} }
SurfaceInterval GetInterval() const { SurfaceInterval GetInterval() const {
return SurfaceInterval::right_open(addr, end); return SurfaceInterval::right_open(addr, end);
} }
SurfaceInterval GetSubRectInterval(MathUtil::Rectangle<u32> unscaled_rect) const;
u32 GetScaledWidth() const { u32 GetScaledWidth() const {
return width * res_scale; return width * res_scale;
} }
@ -217,6 +218,22 @@ struct SurfaceParams {
return size * 8 / GetFormatBpp(pixel_format); 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 addr = 0;
PAddr end = 0; PAddr end = 0;
u32 size = 0; u32 size = 0;
@ -227,20 +244,12 @@ struct SurfaceParams {
u16 res_scale = 1; u16 res_scale = 1;
bool is_tiled = false; bool is_tiled = false;
u32 bytes_per_pixel = 0;
PixelFormat pixel_format = PixelFormat::Invalid; PixelFormat pixel_format = PixelFormat::Invalid;
SurfaceType type = SurfaceType::Invalid; SurfaceType type = SurfaceType::Invalid;
}; };
struct CachedSurface : SurfaceParams { 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 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 { bool IsRegionValid(const SurfaceInterval& interval) const {
return (invalid_regions.find(interval) == invalid_regions.end()); return (invalid_regions.find(interval) == invalid_regions.end());
@ -295,7 +304,8 @@ public:
Surface GetTextureSurface(const Pica::TexturingRegs::FullTextureConfig& config); Surface GetTextureSurface(const Pica::TexturingRegs::FullTextureConfig& config);
/// Get the color and depth surfaces based on the framebuffer configuration /// 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 /// Get a surface that matches the fill config
Surface GetFillSurface(const GPU::Regs::MemoryFillConfig& config); Surface GetFillSurface(const GPU::Regs::MemoryFillConfig& config);

View File

@ -69,6 +69,12 @@ OpenGLState::OpenGLState() {
draw.uniform_buffer = 0; draw.uniform_buffer = 0;
draw.shader_program = 0; draw.shader_program = 0;
scissor.enabled = false;
scissor.x = 0;
scissor.y = 0;
scissor.width = 0;
scissor.height = 0;
clip_distance = {}; clip_distance = {};
} }
@ -263,6 +269,22 @@ void OpenGLState::Apply() const {
glUseProgram(draw.shader_program); 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 // Clip distance
for (size_t i = 0; i < clip_distance.size(); ++i) { for (size_t i = 0; i < clip_distance.size(); ++i) {
if (clip_distance[i] != cur_state.clip_distance[i]) { if (clip_distance[i] != cur_state.clip_distance[i]) {

View File

@ -124,6 +124,14 @@ public:
GLuint shader_program; // GL_CURRENT_PROGRAM GLuint shader_program; // GL_CURRENT_PROGRAM
} draw; } 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 std::array<bool, 2> clip_distance; // GL_CLIP_DISTANCE
OpenGLState(); OpenGLState();