mirror of
https://github.com/citra-emu/citra.git
synced 2025-02-18 19:40:10 +00:00
texcache upgrade
This commit is contained in:
parent
5620327e03
commit
e958b2c828
@ -58,7 +58,6 @@ void VMManager::Reset() {
|
||||
|
||||
page_table.pointers.fill(nullptr);
|
||||
page_table.attributes.fill(Memory::PageType::Unmapped);
|
||||
page_table.cached_res_count.fill(0);
|
||||
|
||||
UpdatePageTableForVMA(initial_vma);
|
||||
}
|
||||
|
@ -476,10 +476,11 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
|
||||
// TODO: Consider attempting rasterizer-accelerated surface blit if that usage is ever
|
||||
// possible/likely
|
||||
Memory::RasterizerFlushVirtualRegion(command.dma_request.source_address,
|
||||
command.dma_request.size, Memory::FlushMode::Flush);
|
||||
command.dma_request.size,
|
||||
Memory::FlushMode::Flush);
|
||||
Memory::RasterizerFlushVirtualRegion(command.dma_request.dest_address,
|
||||
command.dma_request.size,
|
||||
Memory::FlushMode::FlushAndInvalidate);
|
||||
Memory::FlushMode::Invalidate);
|
||||
|
||||
// TODO(Subv): These memory accesses should not go through the application's memory mapping.
|
||||
// They should go through the GSP module's memory mapping.
|
||||
|
@ -96,20 +96,11 @@ static void MemoryFill(const Regs::MemoryFillConfig& config) {
|
||||
u8* start = Memory::GetPhysicalPointer(start_addr);
|
||||
u8* end = Memory::GetPhysicalPointer(end_addr);
|
||||
|
||||
// TODO: Consider always accelerating and returning vector of
|
||||
// regions that the accelerated fill did not cover to
|
||||
// reduce/eliminate the fill that the cpu has to do.
|
||||
// This would also mean that the flush below is not needed.
|
||||
// Fill should first flush all surfaces that touch but are
|
||||
// not completely within the fill range.
|
||||
// Then fill all completely covered surfaces, and return the
|
||||
// regions that were between surfaces or within the touching
|
||||
// ones for cpu to manually fill here.
|
||||
if (VideoCore::g_renderer->Rasterizer()->AccelerateFill(config))
|
||||
return;
|
||||
|
||||
Memory::RasterizerFlushAndInvalidateRegion(config.GetStartAddress(),
|
||||
config.GetEndAddress() - config.GetStartAddress());
|
||||
Memory::RasterizerInvalidateRegion(config.GetStartAddress(),
|
||||
config.GetEndAddress() - config.GetStartAddress());
|
||||
|
||||
if (config.fill_24bit) {
|
||||
// fill with 24-bit values
|
||||
@ -199,7 +190,7 @@ static void DisplayTransfer(const Regs::DisplayTransferConfig& config) {
|
||||
u32 output_size = output_width * output_height * GPU::Regs::BytesPerPixel(config.output_format);
|
||||
|
||||
Memory::RasterizerFlushRegion(config.GetPhysicalInputAddress(), input_size);
|
||||
Memory::RasterizerFlushAndInvalidateRegion(config.GetPhysicalOutputAddress(), output_size);
|
||||
Memory::RasterizerInvalidateRegion(config.GetPhysicalOutputAddress(), output_size);
|
||||
|
||||
for (u32 y = 0; y < output_height; ++y) {
|
||||
for (u32 x = 0; x < output_width; ++x) {
|
||||
@ -363,8 +354,12 @@ static void TextureCopy(const Regs::DisplayTransferConfig& config) {
|
||||
|
||||
size_t contiguous_output_size =
|
||||
config.texture_copy.size / output_width * (output_width + output_gap);
|
||||
Memory::RasterizerFlushAndInvalidateRegion(config.GetPhysicalOutputAddress(),
|
||||
static_cast<u32>(contiguous_output_size));
|
||||
// Only need to flush output if it has a gap
|
||||
const auto FlushInvalidate_fn = (output_gap != 0) ?
|
||||
Memory::RasterizerFlushAndInvalidateRegion :
|
||||
Memory::RasterizerInvalidateRegion;
|
||||
FlushInvalidate_fn(config.GetPhysicalOutputAddress(),
|
||||
static_cast<u32>(contiguous_output_size));
|
||||
|
||||
u32 remaining_input = input_width;
|
||||
u32 remaining_output = output_width;
|
||||
|
@ -50,7 +50,6 @@ static void MapPages(PageTable& page_table, u32 base, u32 size, u8* memory, Page
|
||||
|
||||
page_table.attributes[base] = type;
|
||||
page_table.pointers[base] = memory;
|
||||
page_table.cached_res_count[base] = 0;
|
||||
|
||||
base += 1;
|
||||
if (memory != nullptr)
|
||||
@ -187,7 +186,7 @@ void Write(const VAddr vaddr, const T data) {
|
||||
ASSERT_MSG(false, "Mapped memory page without a pointer @ %08X", vaddr);
|
||||
break;
|
||||
case PageType::RasterizerCachedMemory: {
|
||||
RasterizerFlushVirtualRegion(vaddr, sizeof(T), FlushMode::FlushAndInvalidate);
|
||||
RasterizerFlushVirtualRegion(vaddr, sizeof(T), FlushMode::Invalidate);
|
||||
std::memcpy(GetPointerFromVMA(vaddr), &data, sizeof(T));
|
||||
break;
|
||||
}
|
||||
@ -195,7 +194,7 @@ void Write(const VAddr vaddr, const T data) {
|
||||
WriteMMIO<T>(GetMMIOHandler(vaddr), vaddr, data);
|
||||
break;
|
||||
case PageType::RasterizerCachedSpecial: {
|
||||
RasterizerFlushVirtualRegion(vaddr, sizeof(T), FlushMode::FlushAndInvalidate);
|
||||
RasterizerFlushVirtualRegion(vaddr, sizeof(T), FlushMode::Invalidate);
|
||||
WriteMMIO<T>(GetMMIOHandler(vaddr), vaddr, data);
|
||||
break;
|
||||
}
|
||||
@ -315,7 +314,7 @@ u8* GetPhysicalPointer(PAddr address) {
|
||||
return target_pointer;
|
||||
}
|
||||
|
||||
void RasterizerMarkRegionCached(PAddr start, u32 size, int count_delta) {
|
||||
void RasterizerMarkRegionCached(PAddr start, u32 size, bool cached) {
|
||||
if (start == 0) {
|
||||
return;
|
||||
}
|
||||
@ -336,14 +335,10 @@ void RasterizerMarkRegionCached(PAddr start, u32 size, int count_delta) {
|
||||
}
|
||||
VAddr vaddr = *maybe_vaddr;
|
||||
|
||||
u8& res_count = current_page_table->cached_res_count[vaddr >> PAGE_BITS];
|
||||
ASSERT_MSG(count_delta <= UINT8_MAX - res_count,
|
||||
"Rasterizer resource cache counter overflow!");
|
||||
ASSERT_MSG(count_delta >= -res_count, "Rasterizer resource cache counter underflow!");
|
||||
PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS];
|
||||
|
||||
// Switch page type to cached if now cached
|
||||
if (res_count == 0) {
|
||||
PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS];
|
||||
if (cached) {
|
||||
// Switch page type to cached
|
||||
switch (page_type) {
|
||||
case PageType::Unmapped:
|
||||
// It is not necessary for a process to have this region mapped into its address
|
||||
@ -360,12 +355,8 @@ void RasterizerMarkRegionCached(PAddr start, u32 size, int count_delta) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
res_count += count_delta;
|
||||
|
||||
// Switch page type to uncached if now uncached
|
||||
if (res_count == 0) {
|
||||
PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS];
|
||||
else {
|
||||
// Switch page type to uncached
|
||||
switch (page_type) {
|
||||
case PageType::Unmapped:
|
||||
// It is not necessary for a process to have this region mapped into its address
|
||||
@ -400,6 +391,12 @@ void RasterizerFlushRegion(PAddr start, u32 size) {
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerInvalidateRegion(PAddr start, u32 size) {
|
||||
if (VideoCore::g_renderer != nullptr) {
|
||||
VideoCore::g_renderer->Rasterizer()->InvalidateRegion(start, size);
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerFlushAndInvalidateRegion(PAddr start, u32 size) {
|
||||
// Since pages are unmapped on shutdown after video core is shutdown, the renderer may be
|
||||
// null here
|
||||
@ -431,6 +428,9 @@ void RasterizerFlushVirtualRegion(VAddr start, u32 size, FlushMode mode) {
|
||||
case FlushMode::Flush:
|
||||
rasterizer->FlushRegion(physical_start, overlap_size);
|
||||
break;
|
||||
case FlushMode::Invalidate:
|
||||
rasterizer->InvalidateRegion(physical_start, overlap_size);
|
||||
break;
|
||||
case FlushMode::FlushAndInvalidate:
|
||||
rasterizer->FlushAndInvalidateRegion(physical_start, overlap_size);
|
||||
break;
|
||||
@ -556,13 +556,13 @@ void WriteBlock(const VAddr dest_addr, const void* src_buffer, const size_t size
|
||||
break;
|
||||
}
|
||||
case PageType::RasterizerCachedMemory: {
|
||||
RasterizerFlushVirtualRegion(current_vaddr, copy_amount, FlushMode::FlushAndInvalidate);
|
||||
RasterizerFlushVirtualRegion(current_vaddr, copy_amount, FlushMode::Invalidate);
|
||||
std::memcpy(GetPointerFromVMA(current_vaddr), src_buffer, copy_amount);
|
||||
break;
|
||||
}
|
||||
case PageType::RasterizerCachedSpecial: {
|
||||
DEBUG_ASSERT(GetMMIOHandler(current_vaddr));
|
||||
RasterizerFlushVirtualRegion(current_vaddr, copy_amount, FlushMode::FlushAndInvalidate);
|
||||
RasterizerFlushVirtualRegion(current_vaddr, copy_amount, FlushMode::Invalidate);
|
||||
GetMMIOHandler(current_vaddr)->WriteBlock(current_vaddr, src_buffer, copy_amount);
|
||||
break;
|
||||
}
|
||||
@ -608,13 +608,13 @@ void ZeroBlock(const VAddr dest_addr, const size_t size) {
|
||||
break;
|
||||
}
|
||||
case PageType::RasterizerCachedMemory: {
|
||||
RasterizerFlushVirtualRegion(current_vaddr, copy_amount, FlushMode::FlushAndInvalidate);
|
||||
RasterizerFlushVirtualRegion(current_vaddr, copy_amount, FlushMode::Invalidate);
|
||||
std::memset(GetPointerFromVMA(current_vaddr), 0, copy_amount);
|
||||
break;
|
||||
}
|
||||
case PageType::RasterizerCachedSpecial: {
|
||||
DEBUG_ASSERT(GetMMIOHandler(current_vaddr));
|
||||
RasterizerFlushVirtualRegion(current_vaddr, copy_amount, FlushMode::FlushAndInvalidate);
|
||||
RasterizerFlushVirtualRegion(current_vaddr, copy_amount, FlushMode::Invalidate);
|
||||
GetMMIOHandler(current_vaddr)->WriteBlock(current_vaddr, zeros.data(), copy_amount);
|
||||
break;
|
||||
}
|
||||
|
@ -68,12 +68,6 @@ struct PageTable {
|
||||
* the corresponding entry in `pointers` MUST be set to null.
|
||||
*/
|
||||
std::array<PageType, PAGE_TABLE_NUM_ENTRIES> attributes;
|
||||
|
||||
/**
|
||||
* Indicates the number of externally cached resources touching a page that should be
|
||||
* flushed before the memory is accessed
|
||||
*/
|
||||
std::array<u8, PAGE_TABLE_NUM_ENTRIES> cached_res_count;
|
||||
};
|
||||
|
||||
/// Physical memory regions as seen from the ARM11
|
||||
@ -232,16 +226,20 @@ boost::optional<VAddr> PhysicalToVirtualAddress(PAddr addr);
|
||||
u8* GetPhysicalPointer(PAddr address);
|
||||
|
||||
/**
|
||||
* Adds the supplied value to the rasterizer resource cache counter of each
|
||||
* page touching the region.
|
||||
* Mark each page touching the region as cached.
|
||||
*/
|
||||
void RasterizerMarkRegionCached(PAddr start, u32 size, int count_delta);
|
||||
void RasterizerMarkRegionCached(PAddr start, u32 size, bool cached);
|
||||
|
||||
/**
|
||||
* Flushes any externally cached rasterizer resources touching the given region.
|
||||
*/
|
||||
* Flushes any externally cached rasterizer resources touching the given region.
|
||||
*/
|
||||
void RasterizerFlushRegion(PAddr start, u32 size);
|
||||
|
||||
/**
|
||||
* Invalidates any externally cached rasterizer resources touching the given region.
|
||||
*/
|
||||
void RasterizerInvalidateRegion(PAddr start, u32 size);
|
||||
|
||||
/**
|
||||
* Flushes and invalidates any externally cached rasterizer resources touching the given region.
|
||||
*/
|
||||
@ -250,6 +248,8 @@ void RasterizerFlushAndInvalidateRegion(PAddr start, u32 size);
|
||||
enum class FlushMode {
|
||||
/// Write back modified surfaces to RAM
|
||||
Flush,
|
||||
/// Remove region from the cache
|
||||
Invalidate,
|
||||
/// Write back modified surfaces to RAM, and also remove them from the cache
|
||||
FlushAndInvalidate,
|
||||
};
|
||||
|
@ -16,7 +16,6 @@ TestEnvironment::TestEnvironment(bool mutable_memory_)
|
||||
|
||||
page_table.pointers.fill(nullptr);
|
||||
page_table.attributes.fill(Memory::PageType::Unmapped);
|
||||
page_table.cached_res_count.fill(0);
|
||||
|
||||
Memory::MapIoRegion(page_table, 0x00000000, 0x80000000, test_memory);
|
||||
Memory::MapIoRegion(page_table, 0x80000000, 0x80000000, test_memory);
|
||||
|
@ -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;
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include <utility>
|
||||
#include <glad/glad.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 +22,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));
|
||||
@ -225,12 +227,27 @@ void RasterizerOpenGL::DrawTriangles() {
|
||||
MICROPROFILE_SCOPE(OpenGL_Drawing);
|
||||
const auto& regs = Pica::g_state.regs;
|
||||
|
||||
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.write_mask == GL_TRUE ||
|
||||
(has_stencil && 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 &&
|
||||
(state.depth.test_enabled || write_depth_fb);
|
||||
|
||||
// Sync and bind the framebuffer surfaces
|
||||
CachedSurface* color_surface;
|
||||
CachedSurface* depth_surface;
|
||||
Surface color_surface;
|
||||
Surface depth_surface;
|
||||
MathUtil::Rectangle<int> rect;
|
||||
std::tie(color_surface, depth_surface, rect) =
|
||||
res_cache.GetFramebufferSurfaces(regs.framebuffer.framebuffer);
|
||||
res_cache.GetFramebufferSurfaces(using_color_fb, using_depth_fb);
|
||||
|
||||
state.draw.draw_framebuffer = framebuffer.handle;
|
||||
state.Apply();
|
||||
@ -238,8 +255,7 @@ void RasterizerOpenGL::DrawTriangles() {
|
||||
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);
|
||||
@ -258,37 +274,42 @@ 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;
|
||||
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 float res_scale_width = color_surface != nullptr ? color_surface->res_scale_width :
|
||||
(depth_surface == nullptr ? 1.0f : depth_surface->res_scale_width);
|
||||
const float res_scale_height = color_surface != nullptr ? color_surface->res_scale_height :
|
||||
(depth_surface == nullptr ? 1.0f : depth_surface->res_scale_height);
|
||||
|
||||
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));
|
||||
static_cast<GLint>(rect.left + regs.rasterizer.viewport_corner.x * res_scale_width),
|
||||
static_cast<GLint>(rect.bottom + regs.rasterizer.viewport_corner.y * res_scale_height),
|
||||
static_cast<GLsizei>(viewport_width * res_scale_width),
|
||||
static_cast<GLsizei>(viewport_height * 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) {
|
||||
if (uniform_block_data.data.framebuffer_scale[0] != res_scale_width ||
|
||||
uniform_block_data.data.framebuffer_scale[1] != 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;
|
||||
uniform_block_data.data.framebuffer_scale[0] = res_scale_width;
|
||||
uniform_block_data.data.framebuffer_scale[1] = res_scale_height;
|
||||
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);
|
||||
rect.left + regs.rasterizer.scissor_test.x1 * res_scale_width);
|
||||
GLint scissor_y1 = static_cast<GLint>(
|
||||
rect.bottom + regs.rasterizer.scissor_test.y1 * color_surface->res_scale_height);
|
||||
rect.bottom + regs.rasterizer.scissor_test.y1 * res_scale_height);
|
||||
// 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);
|
||||
rect.left + (regs.rasterizer.scissor_test.x2 + 1) * res_scale_width);
|
||||
GLint scissor_y2 = static_cast<GLint>(
|
||||
rect.bottom + (regs.rasterizer.scissor_test.y2 + 1) * color_surface->res_scale_height);
|
||||
rect.bottom + (regs.rasterizer.scissor_test.y2 + 1) * res_scale_height);
|
||||
|
||||
if (uniform_block_data.data.scissor_x1 != scissor_x1 ||
|
||||
uniform_block_data.data.scissor_x2 != scissor_x2 ||
|
||||
@ -309,7 +330,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 {
|
||||
@ -386,14 +407,27 @@ void RasterizerOpenGL::DrawTriangles() {
|
||||
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);
|
||||
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) {
|
||||
depth_surface->dirty = true;
|
||||
res_cache.FlushRegion(depth_surface->addr, depth_surface->size, depth_surface, true);
|
||||
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();
|
||||
@ -891,227 +925,119 @@ 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) {
|
||||
Surface src_surface;
|
||||
std::tie(src_surface, src_rect) = res_cache.GetSurfaceSubRect(src_params, false, 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);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
Surface dst_surface;
|
||||
std::tie(dst_surface, dst_rect) = res_cache.GetSurfaceSubRect(dst_params, false, 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 (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;
|
||||
const u32 input_width = config.texture_copy.input_width * 16;
|
||||
const u32 input_gap = config.texture_copy.input_gap * 16;
|
||||
const u32 output_width = config.texture_copy.output_width * 16;
|
||||
const u32 output_gap = config.texture_copy.output_gap * 16;
|
||||
|
||||
if (config.texture_copy.size == 0)
|
||||
return true;
|
||||
|
||||
if (input_width != output_width || config.texture_copy.size % input_width != 0)
|
||||
return false;
|
||||
|
||||
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 = config.texture_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<int> 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 * 8) % SurfaceParams::GetFormatBpp(src_surface->pixel_format) != 0 ||
|
||||
(src_surface->is_tiled && src_surface->PixelsInBytes(output_gap) % 64 != 0))
|
||||
return false;
|
||||
|
||||
SurfaceParams dst_params = *src_surface;
|
||||
dst_params.addr = config.GetPhysicalOutputAddress();
|
||||
dst_params.stride = (output_width + output_gap) * src_surface->stride / src_params.stride;
|
||||
dst_params.width = output_width * src_surface->stride / src_params.stride;
|
||||
dst_params.height = src_surface->is_tiled ? src_params.height * 8 : src_params.height;
|
||||
dst_params.UpdateParams();
|
||||
|
||||
const bool load_gap = output_gap != 0; // Since we are going to invalidate the gap if there is one, we will have to load it first
|
||||
MathUtil::Rectangle<int> dst_rect;
|
||||
Surface dst_surface;
|
||||
std::tie(dst_surface, dst_rect) = res_cache.GetSurfaceSubRect(dst_params, false, load_gap);
|
||||
if (src_surface == nullptr)
|
||||
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) {
|
||||
MICROPROFILE_SCOPE(OpenGL_Blits);
|
||||
using PixelFormat = CachedSurface::PixelFormat;
|
||||
using SurfaceType = CachedSurface::SurfaceType;
|
||||
|
||||
CachedSurface* dst_surface = res_cache.TryGetFillSurface(config);
|
||||
|
||||
if (dst_surface == nullptr) {
|
||||
Surface dst_surface = res_cache.GetFillSurface(config);
|
||||
if (dst_surface == nullptr)
|
||||
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);
|
||||
}
|
||||
|
||||
cur_state.draw.draw_framebuffer = old_fb;
|
||||
// TODO: Return scissor test to previous value when scissor test is implemented
|
||||
cur_state.Apply();
|
||||
|
||||
dst_surface->dirty = true;
|
||||
res_cache.FlushRegion(dst_surface->addr, dst_surface->size, dst_surface, true);
|
||||
res_cache.InvalidateRegion(dst_surface->addr, dst_surface->size, dst_surface);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1123,16 +1049,18 @@ 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);
|
||||
Surface src_surface;
|
||||
std::tie(src_surface, src_rect) = res_cache.GetSurfaceSubRect(src_params, false, true);
|
||||
|
||||
if (src_surface == nullptr) {
|
||||
return false;
|
||||
|
@ -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;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -12,6 +12,7 @@
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-local-typedef"
|
||||
#endif
|
||||
#include <boost/icl/interval_set.hpp>
|
||||
#include <boost/icl/interval_map.hpp>
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
@ -20,21 +21,32 @@
|
||||
#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>;
|
||||
|
||||
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<int>>;
|
||||
using SurfaceSurfaceRect_Tuple = std::tuple<Surface, Surface, MathUtil::Rectangle<int>>;
|
||||
|
||||
using PageMap = boost::icl::interval_map<u32, int>;
|
||||
|
||||
struct SurfaceParams {
|
||||
explicit SurfaceParams();
|
||||
|
||||
struct CachedSurface {
|
||||
enum class PixelFormat {
|
||||
// First 5 formats are shared between textures and color buffers
|
||||
RGBA8 = 0,
|
||||
@ -68,10 +80,11 @@ struct CachedSurface {
|
||||
Texture = 1,
|
||||
Depth = 2,
|
||||
DepthStencil = 3,
|
||||
Invalid = 4,
|
||||
Fill = 4,
|
||||
Invalid = 5
|
||||
};
|
||||
|
||||
static unsigned int GetFormatBpp(CachedSurface::PixelFormat format) {
|
||||
static unsigned int GetFormatBpp(SurfaceParams::PixelFormat format) {
|
||||
static const std::array<unsigned int, 18> bpp_table = {
|
||||
32, // RGBA8
|
||||
24, // RGB8
|
||||
@ -162,31 +175,98 @@ struct CachedSurface {
|
||||
return SurfaceType::Invalid;
|
||||
}
|
||||
|
||||
/// Update the params "size", "end", "bytes_per_pixel" and "type" from the already set "addr", "width", "height" and "pixel_format"
|
||||
void UpdateParams() {
|
||||
size = width * height * GetFormatBpp(pixel_format) / 8;
|
||||
|
||||
if (stride == 0)
|
||||
stride = width;
|
||||
else
|
||||
size += (stride - width) * (height - 1) * GetFormatBpp(pixel_format) / 8;
|
||||
|
||||
end = addr + size;
|
||||
type = GetFormatType(pixel_format);
|
||||
bytes_per_pixel = GetFormatBpp(pixel_format) / 8;
|
||||
}
|
||||
|
||||
SurfaceInterval GetInterval() const {
|
||||
return SurfaceInterval::right_open(addr, end);
|
||||
}
|
||||
|
||||
u32 GetScaledWidth() const {
|
||||
return (u32)(width * res_scale_width);
|
||||
return static_cast<u32>(width * res_scale_width);
|
||||
}
|
||||
|
||||
u32 GetScaledHeight() const {
|
||||
return (u32)(height * res_scale_height);
|
||||
return static_cast<u32>(height * res_scale_height);
|
||||
}
|
||||
|
||||
PAddr addr;
|
||||
u32 size;
|
||||
MathUtil::Rectangle<int> GetRect() const {
|
||||
return MathUtil::Rectangle<int>(0, 0, width, height);
|
||||
}
|
||||
|
||||
PAddr min_valid;
|
||||
PAddr max_valid;
|
||||
MathUtil::Rectangle<int> GetScaledRect() const {
|
||||
return MathUtil::Rectangle<int>(0, 0, GetScaledWidth(), GetScaledHeight());
|
||||
}
|
||||
|
||||
OGLTexture texture;
|
||||
u32 width;
|
||||
u32 height;
|
||||
/// Stride between lines, in pixels. Only valid for images in linear format.
|
||||
u32 pixel_stride = 0;
|
||||
u32 PixelsInBytes(u32 size) const {
|
||||
return size * 8 / GetFormatBpp(pixel_format);
|
||||
}
|
||||
|
||||
PAddr addr = 0;
|
||||
PAddr end = 0;
|
||||
u32 size = 0;
|
||||
|
||||
u32 width = 0;
|
||||
u32 height = 0;
|
||||
u32 stride = 0;
|
||||
float res_scale_width = 1.f;
|
||||
float res_scale_height = 1.f;
|
||||
|
||||
bool is_tiled;
|
||||
PixelFormat pixel_format;
|
||||
bool dirty;
|
||||
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;
|
||||
|
||||
MathUtil::Rectangle<int> GetSubRect(const SurfaceParams& sub_surface) const;
|
||||
MathUtil::Rectangle<int> GetScaledSubRect(const SurfaceParams& sub_surface) const;
|
||||
|
||||
bool IsRegionValid(const SurfaceInterval& interval) const {
|
||||
return (invalid_regions.find(interval) == invalid_regions.end());
|
||||
}
|
||||
|
||||
bool IsRegionPartiallyValid(const SurfaceInterval& interval) const {
|
||||
const auto it = invalid_regions.find(interval);
|
||||
if (it == invalid_regions.end())
|
||||
return true;
|
||||
return ((boost::icl::first(*it) > addr) || (boost::icl::last_next(*it) < end));
|
||||
}
|
||||
|
||||
SurfaceRegions invalid_regions;
|
||||
|
||||
u32 fill_size = 0; /// Number of bytes to read from fill_data
|
||||
std::array<u8, 4> fill_data;
|
||||
|
||||
OGLTexture texture;
|
||||
|
||||
u32 gl_bytes_per_pixel;
|
||||
int gl_buffer_offset;
|
||||
std::vector<u8> gl_buffer;
|
||||
bool gl_buffer_dirty;
|
||||
|
||||
// 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();
|
||||
void DownloadGLTexture();
|
||||
};
|
||||
|
||||
class RasterizerCacheOpenGL : NonCopyable {
|
||||
@ -194,46 +274,56 @@ 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<int>& src_rect,
|
||||
const Surface& dst_surface, const MathUtil::Rectangle<int>& 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);
|
||||
|
||||
/// 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);
|
||||
/// Load a texture from 3DS memory to OpenGL and cache it (if not already cached)
|
||||
Surface GetSurface(const SurfaceParams& params, bool 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, bool 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);
|
||||
|
||||
/// 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);
|
||||
|
||||
/// 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:
|
||||
/// 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];
|
||||
SurfaceMap dirty_regions;
|
||||
PageMap cached_pages;
|
||||
};
|
||||
|
@ -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 {}
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user