rasterizer_cache: Use PBO staging buffer cache for texture uploads/downloads
This commit is contained in:
@ -116,7 +116,6 @@ public:
|
||||
QSurfaceFormat format;
|
||||
format.setVersion(4, 4);
|
||||
format.setProfile(QSurfaceFormat::CoreProfile);
|
||||
format.setOption(QSurfaceFormat::DebugContext);
|
||||
// TODO: expose a setting for buffer value (ie default/single/double/triple)
|
||||
format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
|
||||
format.setSwapInterval(0);
|
||||
|
@ -4,14 +4,12 @@
|
||||
|
||||
#include "common/microprofile.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/rasterizer_cache/cached_surface.h"
|
||||
#include "video_core/rasterizer_cache/rasterizer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/renderer_opengl/gl_vars.h"
|
||||
#include "video_core/renderer_opengl/texture_downloader_es.h"
|
||||
#include "video_core/renderer_opengl/texture_filters/texture_filterer.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
@ -22,89 +20,8 @@ CachedSurface::~CachedSurface() {
|
||||
}
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(RasterizerCache_SurfaceLoad, "RasterizerCache", "Surface Load", MP_RGB(128, 192, 64));
|
||||
void CachedSurface::LoadGLBuffer(PAddr load_start, PAddr load_end) {
|
||||
DEBUG_ASSERT(load_start >= addr && load_end <= end);
|
||||
|
||||
auto source_ptr = VideoCore::g_memory->GetPhysicalRef(load_start);
|
||||
if (!source_ptr) [[unlikely]] {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto start_offset = load_start - addr;
|
||||
const auto upload_data = source_ptr.GetWriteBytes(load_end - load_start);
|
||||
const auto upload_size = static_cast<u32>(upload_data.size());
|
||||
|
||||
if (gl_buffer.empty()) {
|
||||
gl_buffer.resize(width * height * GetBytesPerPixel(pixel_format));
|
||||
}
|
||||
|
||||
MICROPROFILE_SCOPE(RasterizerCache_SurfaceLoad);
|
||||
|
||||
if (!is_tiled) {
|
||||
ASSERT(type == SurfaceType::Color);
|
||||
|
||||
const auto dest_buffer = std::span{gl_buffer.begin() + start_offset, upload_size};
|
||||
if (pixel_format == PixelFormat::RGBA8 && GLES) {
|
||||
Pica::Texture::ConvertABGRToRGBA(upload_data, dest_buffer);
|
||||
} else if (pixel_format == PixelFormat::RGB8 && GLES) {
|
||||
Pica::Texture::ConvertBGRToRGB(upload_data, dest_buffer);
|
||||
} else {
|
||||
std::memcpy(dest_buffer.data(), upload_data.data(), upload_size);
|
||||
}
|
||||
} else {
|
||||
UnswizzleTexture(*this, start_offset, upload_data, gl_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(RasterizerCache_SurfaceFlush, "RasterizerCache", "Surface Flush", MP_RGB(128, 192, 64));
|
||||
void CachedSurface::FlushGLBuffer(PAddr flush_start, PAddr flush_end) {
|
||||
DEBUG_ASSERT(flush_start >= addr && flush_end <= end);
|
||||
|
||||
auto dest_ptr = VideoCore::g_memory->GetPhysicalRef(flush_start);
|
||||
if (!dest_ptr) [[unlikely]] {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto start_offset = flush_start - addr;
|
||||
const auto download_dest = dest_ptr.GetWriteBytes(flush_end - flush_start);
|
||||
const auto download_size = static_cast<u32>(download_dest.size());
|
||||
|
||||
MICROPROFILE_SCOPE(RasterizerCache_SurfaceFlush);
|
||||
|
||||
if (type == SurfaceType::Fill) {
|
||||
const u32 coarse_start_offset = start_offset - (start_offset % fill_size);
|
||||
const u32 backup_bytes = start_offset % fill_size;
|
||||
std::array<u8, 4> backup_data;
|
||||
if (backup_bytes) {
|
||||
std::memcpy(backup_data.data(), &dest_ptr[coarse_start_offset], backup_bytes);
|
||||
}
|
||||
|
||||
for (u32 offset = coarse_start_offset; offset < download_size; offset += fill_size) {
|
||||
std::memcpy(&dest_ptr[offset], &fill_data[0],
|
||||
std::min(fill_size, download_size - offset));
|
||||
}
|
||||
|
||||
if (backup_bytes)
|
||||
std::memcpy(&dest_ptr[coarse_start_offset], &backup_data[0], backup_bytes);
|
||||
} else if (!is_tiled) {
|
||||
ASSERT(type == SurfaceType::Color);
|
||||
|
||||
const auto download_data = std::span{gl_buffer.begin() + start_offset, download_size};
|
||||
if (pixel_format == PixelFormat::RGBA8 && GLES) {
|
||||
Pica::Texture::ConvertABGRToRGBA(gl_buffer, download_data);
|
||||
} else if (pixel_format == PixelFormat::RGB8 && GLES) {
|
||||
Pica::Texture::ConvertBGRToRGB(gl_buffer, download_data);
|
||||
} else {
|
||||
std::memcpy(download_dest.data(), download_data.data(), download_size);
|
||||
}
|
||||
} else {
|
||||
SwizzleTexture(*this, start_offset, gl_buffer, download_dest);
|
||||
}
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(RasterizerCache_TextureUL, "RasterizerCache", "Texture Upload", MP_RGB(128, 192, 64));
|
||||
void CachedSurface::UploadGLTexture(Common::Rectangle<u32> rect) {
|
||||
void CachedSurface::UploadTexture(Common::Rectangle<u32> rect, const StagingBuffer& staging) {
|
||||
MICROPROFILE_SCOPE(RasterizerCache_TextureUL);
|
||||
|
||||
// Load data from memory to the surface
|
||||
@ -136,17 +53,23 @@ void CachedSurface::UploadGLTexture(Common::Rectangle<u32> rect) {
|
||||
ASSERT(stride * GetBytesPerPixel(pixel_format) % 4 == 0);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(stride));
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, staging.buffer.handle);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast<GLsizei>(rect.GetWidth()),
|
||||
static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
|
||||
&gl_buffer[buffer_offset]);
|
||||
reinterpret_cast<void*>(buffer_offset));
|
||||
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
staging.Lock();
|
||||
|
||||
cur_state.texture_units[0].texture_2d = old_tex;
|
||||
cur_state.Apply();
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
|
||||
if (res_scale != 1) {
|
||||
auto scaled_rect = rect;
|
||||
scaled_rect.left *= res_scale;
|
||||
@ -178,22 +101,17 @@ void CachedSurface::UploadGLTexture(Common::Rectangle<u32> rect) {
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(RasterizerCache_TextureDL, "RasterizerCache", "Texture Download", MP_RGB(128, 192, 64));
|
||||
void CachedSurface::DownloadGLTexture(const Common::Rectangle<u32>& rect) {
|
||||
void CachedSurface::DownloadTexture(Common::Rectangle<u32> rect, const StagingBuffer& staging) {
|
||||
MICROPROFILE_SCOPE(RasterizerCache_TextureDL);
|
||||
|
||||
OpenGLState state = OpenGLState::GetCurState();
|
||||
OpenGLState prev_state = state;
|
||||
SCOPE_EXIT({ prev_state.Apply(); });
|
||||
|
||||
const u32 download_size = width * height * GetBytesPerPixel(pixel_format);
|
||||
|
||||
if (gl_buffer.empty()) {
|
||||
gl_buffer.resize(download_size);
|
||||
}
|
||||
|
||||
// Ensure no bad interactions with GL_PACK_ALIGNMENT
|
||||
ASSERT(stride * GetBytesPerPixel(pixel_format) % 4 == 0);
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(stride));
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, staging.buffer.handle);
|
||||
const u32 buffer_offset = (rect.bottom * stride + rect.left) * GetBytesPerPixel(pixel_format);
|
||||
|
||||
// If not 1x scale, blit scaled texture to a new 1x texture and use that to flush
|
||||
@ -232,11 +150,12 @@ void CachedSurface::DownloadGLTexture(const Common::Rectangle<u32>& rect) {
|
||||
if (GLES) {
|
||||
owner.texture_downloader_es->GetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type,
|
||||
rect.GetHeight(), rect.GetWidth(),
|
||||
&gl_buffer[buffer_offset]);
|
||||
reinterpret_cast<void*>(buffer_offset));
|
||||
} else {
|
||||
glGetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, &gl_buffer[buffer_offset]);
|
||||
glGetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, reinterpret_cast<void*>(buffer_offset));
|
||||
}
|
||||
} else {
|
||||
const u32 download_size = width * height * GetBytesPerPixel(pixel_format);
|
||||
const BufferTextureCopy texture_download = {
|
||||
.buffer_offset = buffer_offset,
|
||||
.buffer_size = download_size,
|
||||
@ -248,9 +167,10 @@ void CachedSurface::DownloadGLTexture(const Common::Rectangle<u32>& rect) {
|
||||
.texture_extent = {rect.GetWidth(), rect.GetHeight()}
|
||||
};
|
||||
|
||||
runtime.ReadTexture(texture, texture_download, pixel_format, gl_buffer);
|
||||
runtime.ReadTexture(texture, texture_download, pixel_format, staging.mapped);
|
||||
}
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
||||
}
|
||||
|
||||
|
@ -44,6 +44,7 @@ private:
|
||||
};
|
||||
|
||||
class RasterizerCache;
|
||||
class StagingBuffer;
|
||||
|
||||
class CachedSurface : public SurfaceParams, public std::enable_shared_from_this<CachedSurface> {
|
||||
public:
|
||||
@ -51,13 +52,9 @@ public:
|
||||
: SurfaceParams(params), owner(owner), runtime(runtime) {}
|
||||
~CachedSurface();
|
||||
|
||||
/// 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(Common::Rectangle<u32> rect);
|
||||
void DownloadGLTexture(const Common::Rectangle<u32>& rect);
|
||||
void UploadTexture(Common::Rectangle<u32> rect, const StagingBuffer& staging);
|
||||
void DownloadTexture(Common::Rectangle<u32> rect, const StagingBuffer& staging);
|
||||
|
||||
bool CanFill(const SurfaceParams& dest_surface, SurfaceInterval fill_interval) const;
|
||||
bool CanCopy(const SurfaceParams& dest_surface, SurfaceInterval copy_interval) const;
|
||||
@ -100,7 +97,6 @@ public:
|
||||
public:
|
||||
bool registered = false;
|
||||
SurfaceRegions invalid_regions;
|
||||
std::vector<std::byte> gl_buffer;
|
||||
|
||||
// Number of bytes to read from fill_data
|
||||
u32 fill_size = 0;
|
||||
|
@ -203,7 +203,7 @@ static void MortonCopy(u32 stride, u32 height, u32 start_offset,
|
||||
// the tile affected to a temporary buffer and copy the part we are interested in
|
||||
if (start_offset < aligned_start_offset && !morton_to_linear) {
|
||||
std::array<std::byte, tile_size> tmp_buf;
|
||||
auto linear_data = linear_buffer.subspan(linear_offset, linear_tile_size);
|
||||
auto linear_data = linear_buffer.last(linear_buffer.size_bytes() - linear_offset);
|
||||
MortonCopyTile<morton_to_linear, format>(stride, tmp_buf, linear_data);
|
||||
|
||||
std::memcpy(tiled_buffer.data(), tmp_buf.data() + start_offset - aligned_down_start_offset,
|
||||
@ -215,7 +215,7 @@ static void MortonCopy(u32 stride, u32 height, u32 start_offset,
|
||||
|
||||
const u32 buffer_end = tiled_offset + aligned_end_offset - aligned_start_offset;
|
||||
while (tiled_offset < buffer_end) {
|
||||
auto linear_data = linear_buffer.subspan(linear_offset, linear_tile_size);
|
||||
auto linear_data = linear_buffer.last(linear_buffer.size_bytes() - linear_offset);
|
||||
auto tiled_data = tiled_buffer.subspan(tiled_offset, tile_size);
|
||||
MortonCopyTile<morton_to_linear, format>(stride, tiled_data, linear_data);
|
||||
tiled_offset += tile_size;
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "video_core/rasterizer_accelerated.h"
|
||||
#include "video_core/rasterizer_cache/rasterizer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_format_reinterpreter.h"
|
||||
#include "video_core/renderer_opengl/gl_vars.h"
|
||||
#include "video_core/renderer_opengl/texture_downloader_es.h"
|
||||
#include "video_core/renderer_opengl/texture_filters/texture_filterer.h"
|
||||
|
||||
@ -730,11 +731,11 @@ void RasterizerCache::DuplicateSurface(const Surface& src_surface,
|
||||
}
|
||||
|
||||
void RasterizerCache::ValidateSurface(const Surface& surface, PAddr addr, u32 size) {
|
||||
if (size == 0)
|
||||
if (size == 0) [[unlikely]] {
|
||||
return;
|
||||
}
|
||||
|
||||
const SurfaceInterval validate_interval(addr, addr + size);
|
||||
|
||||
if (surface->type == SurfaceType::Fill) {
|
||||
// Sanity check, fill surfaces will always be valid when used
|
||||
ASSERT(surface->IsRegionValid(validate_interval));
|
||||
@ -742,15 +743,16 @@ void RasterizerCache::ValidateSurface(const Surface& surface, PAddr addr, u32 si
|
||||
}
|
||||
|
||||
auto validate_regions = surface->invalid_regions & validate_interval;
|
||||
auto notify_validated = [&](SurfaceInterval interval) {
|
||||
auto NotifyValidated = [&](SurfaceInterval interval) {
|
||||
surface->invalid_regions.erase(interval);
|
||||
validate_regions.erase(interval);
|
||||
};
|
||||
|
||||
while (true) {
|
||||
const auto it = validate_regions.begin();
|
||||
if (it == validate_regions.end())
|
||||
if (it == validate_regions.end()) {
|
||||
break;
|
||||
}
|
||||
|
||||
const auto interval = *it & validate_interval;
|
||||
// Look for a valid surface to copy from
|
||||
@ -761,14 +763,14 @@ void RasterizerCache::ValidateSurface(const Surface& surface, PAddr addr, u32 si
|
||||
if (copy_surface != nullptr) {
|
||||
SurfaceInterval copy_interval = params.GetCopyableInterval(copy_surface);
|
||||
CopySurface(copy_surface, surface, copy_interval);
|
||||
notify_validated(copy_interval);
|
||||
NotifyValidated(copy_interval);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to find surface in cache with different format
|
||||
// that can can be reinterpreted to the requested format.
|
||||
if (ValidateByReinterpretation(surface, params, interval)) {
|
||||
notify_validated(interval);
|
||||
NotifyValidated(interval);
|
||||
continue;
|
||||
}
|
||||
// Could not find a matching reinterpreter, check if we need to implement a
|
||||
@ -788,12 +790,105 @@ void RasterizerCache::ValidateSurface(const Surface& surface, PAddr addr, u32 si
|
||||
|
||||
// Load data from 3DS memory
|
||||
FlushRegion(params.addr, params.size);
|
||||
surface->LoadGLBuffer(params.addr, params.end);
|
||||
surface->UploadGLTexture(surface->GetSubRect(params));
|
||||
notify_validated(params.GetInterval());
|
||||
UploadSurface(surface, interval);
|
||||
NotifyValidated(params.GetInterval());
|
||||
}
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(RasterizerCache_SurfaceLoad, "RasterizerCache", "Surface Load", MP_RGB(128, 192, 64));
|
||||
void RasterizerCache::UploadSurface(const Surface& surface, const SurfaceInterval& interval) {
|
||||
const SurfaceParams info = surface->FromInterval(interval);
|
||||
const u32 load_start = info.addr;
|
||||
const u32 load_end = info.end;
|
||||
ASSERT(load_start >= surface->addr && load_end <= surface->end);
|
||||
|
||||
const StagingBuffer& staging = runtime.FindStaging(
|
||||
surface->width * surface->height * GetBytesPerPixel(surface->pixel_format), true);
|
||||
auto source_ptr = VideoCore::g_memory->GetPhysicalRef(info.addr);
|
||||
if (!source_ptr) [[unlikely]] {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto start_offset = load_start - surface->addr;
|
||||
const auto upload_data = source_ptr.GetWriteBytes(load_end - load_start);
|
||||
const auto upload_size = static_cast<u32>(upload_data.size());
|
||||
|
||||
MICROPROFILE_SCOPE(RasterizerCache_SurfaceLoad);
|
||||
|
||||
if (!surface->is_tiled) {
|
||||
ASSERT(surface->type == SurfaceType::Color);
|
||||
|
||||
const auto dest_buffer = staging.mapped.subspan(start_offset, upload_size);
|
||||
if (surface->pixel_format == PixelFormat::RGBA8 && GLES) {
|
||||
Pica::Texture::ConvertABGRToRGBA(upload_data, dest_buffer);
|
||||
} else if (surface->pixel_format == PixelFormat::RGB8 && GLES) {
|
||||
Pica::Texture::ConvertBGRToRGB(upload_data, dest_buffer);
|
||||
} else {
|
||||
std::memcpy(dest_buffer.data(), upload_data.data(), upload_size);
|
||||
}
|
||||
} else {
|
||||
UnswizzleTexture(*surface, start_offset, upload_data, staging.mapped);
|
||||
}
|
||||
|
||||
surface->UploadTexture(surface->GetSubRect(info), staging);
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(RasterizerCache_SurfaceFlush, "RasterizerCache", "Surface Flush", MP_RGB(128, 192, 64));
|
||||
void RasterizerCache::DownloadSurface(const Surface& surface, const SurfaceInterval& interval) {
|
||||
const u32 flush_start = boost::icl::first(interval);
|
||||
const u32 flush_end = boost::icl::last_next(interval);
|
||||
ASSERT(flush_start >= surface->addr && flush_end <= surface->end);
|
||||
|
||||
const StagingBuffer& staging = runtime.FindStaging(
|
||||
surface->width * surface->height * GetBytesPerPixel(surface->pixel_format), false);
|
||||
if (surface->type != SurfaceType::Fill) {
|
||||
SurfaceParams params = surface->FromInterval(interval);
|
||||
surface->DownloadTexture(surface->GetSubRect(params), staging);
|
||||
}
|
||||
|
||||
auto dest_ptr = VideoCore::g_memory->GetPhysicalRef(flush_start);
|
||||
if (!dest_ptr) [[unlikely]] {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto start_offset = flush_start - surface->addr;
|
||||
const auto download_dest = dest_ptr.GetWriteBytes(flush_end - flush_start);
|
||||
const auto download_size = static_cast<u32>(download_dest.size());
|
||||
|
||||
MICROPROFILE_SCOPE(RasterizerCache_SurfaceFlush);
|
||||
|
||||
if (surface->type == SurfaceType::Fill) {
|
||||
const u32 coarse_start_offset = start_offset - (start_offset % surface->fill_size);
|
||||
const u32 backup_bytes = start_offset % surface->fill_size;
|
||||
std::array<u8, 4> backup_data;
|
||||
if (backup_bytes) {
|
||||
std::memcpy(backup_data.data(), &dest_ptr[coarse_start_offset], backup_bytes);
|
||||
}
|
||||
|
||||
for (u32 offset = coarse_start_offset; offset < download_size; offset += surface->fill_size) {
|
||||
std::memcpy(&dest_ptr[offset], &surface->fill_data[0],
|
||||
std::min(surface->fill_size, download_size - offset));
|
||||
}
|
||||
|
||||
if (backup_bytes)
|
||||
std::memcpy(&dest_ptr[coarse_start_offset], &backup_data[0], backup_bytes);
|
||||
} else if (!surface->is_tiled) {
|
||||
ASSERT(surface->type == SurfaceType::Color);
|
||||
|
||||
const auto download_data = staging.mapped.subspan(start_offset, download_size);
|
||||
if (surface->pixel_format == PixelFormat::RGBA8 && GLES) {
|
||||
Pica::Texture::ConvertABGRToRGBA(download_data, download_dest);
|
||||
} else if (surface->pixel_format == PixelFormat::RGB8 && GLES) {
|
||||
Pica::Texture::ConvertBGRToRGB(download_data, download_dest);
|
||||
} else {
|
||||
std::memcpy(download_dest.data(), download_data.data(), download_size);
|
||||
}
|
||||
} else {
|
||||
SwizzleTexture(*surface, start_offset, staging.mapped, download_dest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool RasterizerCache::NoUnimplementedReinterpretations(const Surface& surface,
|
||||
SurfaceParams& params,
|
||||
const SurfaceInterval& interval) {
|
||||
@ -903,8 +998,9 @@ bool RasterizerCache::ValidateByReinterpretation(const Surface& surface,
|
||||
void RasterizerCache::FlushRegion(PAddr addr, u32 size, Surface flush_surface) {
|
||||
std::lock_guard lock{mutex};
|
||||
|
||||
if (size == 0)
|
||||
if (size == 0) [[unlikely]] {
|
||||
return;
|
||||
}
|
||||
|
||||
const SurfaceInterval flush_interval(addr, addr + size);
|
||||
SurfaceRegions flushed_intervals;
|
||||
@ -922,14 +1018,10 @@ void RasterizerCache::FlushRegion(PAddr addr, u32 size, Surface flush_surface) {
|
||||
// Sanity check, this surface is the last one that marked this region dirty
|
||||
ASSERT(surface->IsRegionValid(interval));
|
||||
|
||||
if (surface->type != SurfaceType::Fill) {
|
||||
SurfaceParams params = surface->FromInterval(interval);
|
||||
surface->DownloadGLTexture(surface->GetSubRect(params));
|
||||
}
|
||||
|
||||
surface->FlushGLBuffer(boost::icl::first(interval), boost::icl::last_next(interval));
|
||||
DownloadSurface(surface, interval);
|
||||
flushed_intervals += interval;
|
||||
}
|
||||
|
||||
// Reset dirty regions
|
||||
dirty_regions -= flushed_intervals;
|
||||
}
|
||||
|
@ -102,15 +102,21 @@ private:
|
||||
/// Update surface's texture for given region when necessary
|
||||
void ValidateSurface(const Surface& surface, PAddr addr, u32 size);
|
||||
|
||||
// Returns false if there is a surface in the cache at the interval with the same bit-width,
|
||||
/// Copies pixel data in interval from the guest VRAM to the host GPU surface
|
||||
void UploadSurface(const Surface& surface, const SurfaceInterval& interval);
|
||||
|
||||
/// Copies pixel data in interval from the host GPU surface to the guest VRAM
|
||||
void DownloadSurface(const Surface& surface, const SurfaceInterval& interval);
|
||||
|
||||
/// Returns false if there is a surface in the cache at the interval with the same bit-width,
|
||||
bool NoUnimplementedReinterpretations(const OpenGL::Surface& surface,
|
||||
OpenGL::SurfaceParams& params,
|
||||
const OpenGL::SurfaceInterval& interval);
|
||||
|
||||
// Return true if a surface with an invalid pixel format exists at the interval
|
||||
/// Return true if a surface with an invalid pixel format exists at the interval
|
||||
bool IntervalHasInvalidPixelFormat(SurfaceParams& params, const SurfaceInterval& interval);
|
||||
|
||||
// Attempt to find a reinterpretable surface in the cache and use it to copy for validation
|
||||
/// Attempt to find a reinterpretable surface in the cache and use it to copy for validation
|
||||
bool ValidateByReinterpretation(const Surface& surface, SurfaceParams& params,
|
||||
const SurfaceInterval& interval);
|
||||
|
||||
|
@ -212,46 +212,49 @@ void TextureRuntime::GenerateMipmaps(OGLTexture& texture, u32 max_level) {
|
||||
}
|
||||
|
||||
const StagingBuffer& TextureRuntime::FindStaging(u32 size, bool upload) {
|
||||
const GLenum target = upload ? GL_PIXEL_UNPACK_BUFFER : GL_PIXEL_PACK_BUFFER;
|
||||
const GLbitfield access = upload ? GL_MAP_WRITE_BIT : GL_MAP_READ_BIT;
|
||||
auto& search = upload ? upload_buffers : download_buffers;
|
||||
|
||||
const StagingBuffer key = {
|
||||
.size = size
|
||||
};
|
||||
|
||||
for (auto it = search.lower_bound(key); it != search.end(); it++) {
|
||||
// Attempt to find a free buffer that fits the requested data
|
||||
if (it->IsFree()) {
|
||||
// Attempt to find a free buffer that fits the requested data
|
||||
for (auto it = search.lower_bound({.size = size}); it != search.end(); it++) {
|
||||
if (!upload || it->IsFree()) {
|
||||
return *it;
|
||||
}
|
||||
}
|
||||
|
||||
StagingBuffer staging{};
|
||||
staging.buffer.Create();
|
||||
OGLBuffer buffer{};
|
||||
buffer.Create();
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, staging.buffer.handle);
|
||||
glBindBuffer(target, buffer.handle);
|
||||
|
||||
// Allocate a new buffer and map the data to the host
|
||||
void* data = nullptr;
|
||||
std::byte* data = nullptr;
|
||||
if (driver.IsOpenGLES() && driver.HasExtBufferStorage()) {
|
||||
const GLbitfield storage = upload ? GL_MAP_WRITE_BIT : GL_MAP_READ_BIT | GL_CLIENT_STORAGE_BIT_EXT;
|
||||
glBufferStorageEXT(GL_PIXEL_UNPACK_BUFFER, size, nullptr, storage | GL_MAP_PERSISTENT_BIT_EXT |
|
||||
GL_MAP_COHERENT_BIT_EXT);
|
||||
data = glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, size, access | GL_MAP_PERSISTENT_BIT_EXT |
|
||||
GL_MAP_COHERENT_BIT_EXT);
|
||||
glBufferStorageEXT(target, size, nullptr, storage | GL_MAP_PERSISTENT_BIT_EXT |
|
||||
GL_MAP_COHERENT_BIT_EXT);
|
||||
data = reinterpret_cast<std::byte*>(glMapBufferRange(target, 0, size, access | GL_MAP_PERSISTENT_BIT_EXT |
|
||||
GL_MAP_COHERENT_BIT_EXT));
|
||||
} else if (driver.HasArbBufferStorage()) {
|
||||
const GLbitfield storage = upload ? GL_MAP_WRITE_BIT : GL_MAP_READ_BIT | GL_CLIENT_STORAGE_BIT;
|
||||
glBufferStorage(GL_PIXEL_UNPACK_BUFFER, size, nullptr, storage | GL_MAP_PERSISTENT_BIT |
|
||||
GL_MAP_COHERENT_BIT);
|
||||
data = glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, size, access | GL_MAP_PERSISTENT_BIT |
|
||||
GL_MAP_COHERENT_BIT);
|
||||
glBufferStorage(target, size, nullptr, storage | GL_MAP_PERSISTENT_BIT |
|
||||
GL_MAP_COHERENT_BIT);
|
||||
data = reinterpret_cast<std::byte*>(glMapBufferRange(target, 0, size, access | GL_MAP_PERSISTENT_BIT |
|
||||
GL_MAP_COHERENT_BIT));
|
||||
} else {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
// Insert it to the cache and return the memory
|
||||
staging.mapped = std::span{reinterpret_cast<std::byte*>(data), size};
|
||||
const auto& it = search.insert(std::move(staging));
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
|
||||
StagingBuffer staging = {
|
||||
.buffer = std::move(buffer),
|
||||
.mapped = std::span{data, size},
|
||||
.size = size
|
||||
};
|
||||
|
||||
const auto& it = search.emplace(std::move(staging));
|
||||
return *it;
|
||||
}
|
||||
|
||||
|
@ -13,10 +13,10 @@ namespace OpenGL {
|
||||
struct FormatTuple;
|
||||
|
||||
struct StagingBuffer {
|
||||
u32 size = 0;
|
||||
std::span<std::byte> mapped{};
|
||||
OGLBuffer buffer{};
|
||||
mutable OGLSync buffer_lock{};
|
||||
std::span<std::byte> mapped{};
|
||||
u32 size{};
|
||||
|
||||
bool operator<(const StagingBuffer& other) const {
|
||||
return size < other.size;
|
||||
@ -24,9 +24,13 @@ struct StagingBuffer {
|
||||
|
||||
/// Returns true if the buffer does not take part in pending transfer operations
|
||||
bool IsFree() const {
|
||||
GLint status;
|
||||
glGetSynciv(buffer_lock.handle, GL_SYNC_STATUS, 1, nullptr, &status);
|
||||
return status == GL_SIGNALED;
|
||||
if (buffer_lock) {
|
||||
GLint status;
|
||||
glGetSynciv(buffer_lock.handle, GL_SYNC_STATUS, 1, nullptr, &status);
|
||||
return status == GL_SIGNALED;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Prevents the runtime from reusing the buffer until the transfer operation is complete
|
||||
|
@ -79,7 +79,7 @@ Driver::Driver(bool gles) : is_gles{gles} {
|
||||
* Qualcomm has some spammy info messages that are marked as errors but not important
|
||||
* https://developer.qualcomm.com/comment/11845
|
||||
*/
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
||||
glDebugMessageCallback(DebugHandler, nullptr);
|
||||
#endif
|
||||
|
||||
|
@ -224,9 +224,10 @@ TextureInfo TextureInfo::FromPicaRegister(const TexturingRegs::TextureConfig& co
|
||||
|
||||
void ConvertBGRToRGB(std::span<const std::byte> source, std::span<std::byte> dest) {
|
||||
for (std::size_t i = 0; i < source.size(); i += 3) {
|
||||
dest[i] = source[i + 2];
|
||||
dest[i + 1] = source[i + 1];
|
||||
dest[i + 2] = source[i];
|
||||
u32 bgr{};
|
||||
std::memcpy(&bgr, source.data() + i, 3);
|
||||
const u32 rgb = std::byteswap(bgr << 8);
|
||||
std::memcpy(dest.data(), &rgb, 3);
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user