rasterizer_cache: Add staging buffer cache for uploads/downloads
* In addition bump context version to 4.4 to enforce ARB_buffer_storage and use EXT_buffer_storage for GLES which is support on many mobile devices
This commit is contained in:
2079
externals/glad/include/glad/glad.h
vendored
2079
externals/glad/include/glad/glad.h
vendored
File diff suppressed because it is too large
Load Diff
1033
externals/glad/src/glad.c
vendored
1033
externals/glad/src/glad.c
vendored
File diff suppressed because it is too large
Load Diff
@ -116,7 +116,7 @@ public:
|
|||||||
/// Create the original context that should be shared from
|
/// Create the original context that should be shared from
|
||||||
explicit OpenGLSharedContext(QSurface* surface) : surface(surface) {
|
explicit OpenGLSharedContext(QSurface* surface) : surface(surface) {
|
||||||
QSurfaceFormat format;
|
QSurfaceFormat format;
|
||||||
format.setVersion(4, 3);
|
format.setVersion(4, 4);
|
||||||
format.setProfile(QSurfaceFormat::CoreProfile);
|
format.setProfile(QSurfaceFormat::CoreProfile);
|
||||||
// TODO: expose a setting for buffer value (ie default/single/double/triple)
|
// TODO: expose a setting for buffer value (ie default/single/double/triple)
|
||||||
format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
|
format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <bit>
|
||||||
#include "common/microprofile.h"
|
#include "common/microprofile.h"
|
||||||
#include "common/scope_exit.h"
|
#include "common/scope_exit.h"
|
||||||
#include "common/texture.h"
|
#include "common/texture.h"
|
||||||
@ -152,8 +153,9 @@ void CachedSurface::FlushGLBuffer(PAddr flush_start, PAddr flush_end) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool CachedSurface::LoadCustomTexture(u64 tex_hash) {
|
bool CachedSurface::LoadCustomTexture(u64 tex_hash) {
|
||||||
auto& custom_tex_cache = Core::System::GetInstance().CustomTexCache();
|
Core::System& system = Core::System::GetInstance();
|
||||||
const auto& image_interface = Core::System::GetInstance().GetImageInterface();
|
auto& custom_tex_cache = system.CustomTexCache();
|
||||||
|
const auto& image_interface = system.GetImageInterface();
|
||||||
|
|
||||||
if (custom_tex_cache.IsTextureCached(tex_hash)) {
|
if (custom_tex_cache.IsTextureCached(tex_hash)) {
|
||||||
custom_tex_info = custom_tex_cache.LookupTexture(tex_hash);
|
custom_tex_info = custom_tex_cache.LookupTexture(tex_hash);
|
||||||
@ -171,9 +173,7 @@ bool CachedSurface::LoadCustomTexture(u64 tex_hash) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::bitset<32> width_bits(custom_tex_info.width);
|
if (std::popcount(custom_tex_info.width) != 1 || std::popcount(custom_tex_info.height) != 1) {
|
||||||
const std::bitset<32> height_bits(custom_tex_info.height);
|
|
||||||
if (width_bits.count() != 1 || height_bits.count() != 1) {
|
|
||||||
LOG_ERROR(Render_OpenGL, "Texture {} size is not a power of 2", path_info.path);
|
LOG_ERROR(Render_OpenGL, "Texture {} size is not a power of 2", path_info.path);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -188,20 +188,19 @@ bool CachedSurface::LoadCustomTexture(u64 tex_hash) {
|
|||||||
void CachedSurface::DumpTexture(GLuint target_tex, u64 tex_hash) {
|
void CachedSurface::DumpTexture(GLuint target_tex, u64 tex_hash) {
|
||||||
// Make sure the texture size is a power of 2
|
// Make sure the texture size is a power of 2
|
||||||
// If not, the surface is actually a framebuffer
|
// If not, the surface is actually a framebuffer
|
||||||
std::bitset<32> width_bits(width);
|
if (std::popcount(width) != 1 || std::popcount(height) != 1) {
|
||||||
std::bitset<32> height_bits(height);
|
|
||||||
if (width_bits.count() != 1 || height_bits.count() != 1) {
|
|
||||||
LOG_WARNING(Render_OpenGL, "Not dumping {:016X} because size isn't a power of 2 ({}x{})",
|
LOG_WARNING(Render_OpenGL, "Not dumping {:016X} because size isn't a power of 2 ({}x{})",
|
||||||
tex_hash, width, height);
|
tex_hash, width, height);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dump texture to RGBA8 and encode as PNG
|
// Dump texture to RGBA8 and encode as PNG
|
||||||
const auto& image_interface = Core::System::GetInstance().GetImageInterface();
|
Core::System& system = Core::System::GetInstance();
|
||||||
auto& custom_tex_cache = Core::System::GetInstance().CustomTexCache();
|
const auto& image_interface = system.GetImageInterface();
|
||||||
|
auto& custom_tex_cache = system.CustomTexCache();
|
||||||
std::string dump_path =
|
std::string dump_path =
|
||||||
fmt::format("{}textures/{:016X}/", FileUtil::GetUserPath(FileUtil::UserPath::DumpDir),
|
fmt::format("{}textures/{:016X}/", FileUtil::GetUserPath(FileUtil::UserPath::DumpDir),
|
||||||
Core::System::GetInstance().Kernel().GetCurrentProcess()->codeset->program_id);
|
system.Kernel().GetCurrentProcess()->codeset->program_id);
|
||||||
if (!FileUtil::CreateFullPath(dump_path)) {
|
if (!FileUtil::CreateFullPath(dump_path)) {
|
||||||
LOG_ERROR(Render, "Unable to create {}", dump_path);
|
LOG_ERROR(Render, "Unable to create {}", dump_path);
|
||||||
return;
|
return;
|
||||||
@ -269,7 +268,7 @@ void CachedSurface::UploadGLTexture(Common::Rectangle<u32> rect) {
|
|||||||
// If not 1x scale, create 1x texture that we will blit from to replace texture subrect in
|
// If not 1x scale, create 1x texture that we will blit from to replace texture subrect in
|
||||||
// surface
|
// surface
|
||||||
OGLTexture unscaled_tex;
|
OGLTexture unscaled_tex;
|
||||||
if (res_scale != 1) {
|
if (IsScaled()) {
|
||||||
x0 = 0;
|
x0 = 0;
|
||||||
y0 = 0;
|
y0 = 0;
|
||||||
|
|
||||||
@ -361,37 +360,32 @@ void CachedSurface::UploadGLTexture(Common::Rectangle<u32> rect) {
|
|||||||
MICROPROFILE_DEFINE(RasterizerCache_TextureDL, "RasterizerCache", "Texture Download",
|
MICROPROFILE_DEFINE(RasterizerCache_TextureDL, "RasterizerCache", "Texture Download",
|
||||||
MP_RGB(128, 192, 64));
|
MP_RGB(128, 192, 64));
|
||||||
void CachedSurface::DownloadGLTexture(const Common::Rectangle<u32>& rect) {
|
void CachedSurface::DownloadGLTexture(const Common::Rectangle<u32>& rect) {
|
||||||
if (type == SurfaceType::Fill) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MICROPROFILE_SCOPE(RasterizerCache_TextureDL);
|
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);
|
const u32 download_size = width * height * GetBytesPerPixel(pixel_format);
|
||||||
|
|
||||||
if (gl_buffer.empty()) {
|
if (gl_buffer.empty()) {
|
||||||
gl_buffer.resize(download_size);
|
gl_buffer.resize(download_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
OpenGLState state = OpenGLState::GetCurState();
|
|
||||||
OpenGLState prev_state = state;
|
|
||||||
SCOPE_EXIT({ prev_state.Apply(); });
|
|
||||||
|
|
||||||
// Ensure no bad interactions with GL_PACK_ALIGNMENT
|
// Ensure no bad interactions with GL_PACK_ALIGNMENT
|
||||||
ASSERT(stride * GetBytesPerPixel(pixel_format) % 4 == 0);
|
ASSERT(stride * GetBytesPerPixel(pixel_format) % 4 == 0);
|
||||||
glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(stride));
|
glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(stride));
|
||||||
const u32 buffer_offset = (rect.bottom * stride + rect.left) * GetBytesPerPixel(pixel_format);
|
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
|
// If not 1x scale, blit scaled texture to a new 1x texture and use that to flush
|
||||||
if (res_scale != 1) {
|
if (IsScaled()) {
|
||||||
auto scaled_rect = rect;
|
auto scaled_rect = rect;
|
||||||
scaled_rect.left *= res_scale;
|
scaled_rect.left *= res_scale;
|
||||||
scaled_rect.top *= res_scale;
|
scaled_rect.top *= res_scale;
|
||||||
scaled_rect.right *= res_scale;
|
scaled_rect.right *= res_scale;
|
||||||
scaled_rect.bottom *= res_scale;
|
scaled_rect.bottom *= res_scale;
|
||||||
|
|
||||||
const Common::Rectangle<u32> unscaled_tex_rect{0, rect.GetHeight(), rect.GetWidth(), 0};
|
OGLTexture unscaled_tex = owner.AllocateSurfaceTexture(pixel_format, rect.GetWidth(), rect.GetHeight());
|
||||||
auto unscaled_tex = owner.AllocateSurfaceTexture(pixel_format, rect.GetWidth(), rect.GetHeight());
|
|
||||||
|
|
||||||
const TextureBlit texture_blit = {
|
const TextureBlit texture_blit = {
|
||||||
.surface_type = type,
|
.surface_type = type,
|
||||||
@ -402,8 +396,8 @@ void CachedSurface::DownloadGLTexture(const Common::Rectangle<u32>& rect) {
|
|||||||
.end = {scaled_rect.right, scaled_rect.top}
|
.end = {scaled_rect.right, scaled_rect.top}
|
||||||
},
|
},
|
||||||
.dst_region = Region2D{
|
.dst_region = Region2D{
|
||||||
.start = {unscaled_tex_rect.left, unscaled_tex_rect.bottom},
|
.start = {0, 0},
|
||||||
.end = {unscaled_tex_rect.right, unscaled_tex_rect.top}
|
.end = {rect.GetWidth(), rect.GetHeight()}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -64,8 +64,9 @@ void RasterizerCache::CopySurface(const Surface& src_surface, const Surface& dst
|
|||||||
const ClearValue clear_value =
|
const ClearValue clear_value =
|
||||||
MakeClearValue(dst_surface->type, dst_surface->pixel_format, fill_buffer.data());
|
MakeClearValue(dst_surface->type, dst_surface->pixel_format, fill_buffer.data());
|
||||||
|
|
||||||
const ClearRect clear_rect = {
|
const TextureClear clear_rect = {
|
||||||
.surface_type = dst_surface->type,
|
.surface_type = dst_surface->type,
|
||||||
|
.texture_format = dst_surface->pixel_format,
|
||||||
.texture_level = 0,
|
.texture_level = 0,
|
||||||
.rect = Rect2D{
|
.rect = Rect2D{
|
||||||
.offset = {dst_rect.left, dst_rect.bottom},
|
.offset = {dst_rect.left, dst_rect.bottom},
|
||||||
|
@ -48,6 +48,10 @@ public:
|
|||||||
end = addr + size;
|
end = addr + size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsScaled() const {
|
||||||
|
return res_scale > 1;
|
||||||
|
}
|
||||||
|
|
||||||
SurfaceInterval GetInterval() const {
|
SurfaceInterval GetInterval() const {
|
||||||
return SurfaceInterval(addr, end);
|
return SurfaceInterval(addr, end);
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include "video_core/rasterizer_cache/utils.h"
|
#include "video_core/rasterizer_cache/utils.h"
|
||||||
#include "video_core/rasterizer_cache/texture_runtime.h"
|
#include "video_core/rasterizer_cache/texture_runtime.h"
|
||||||
#include "video_core/renderer_opengl/gl_state.h"
|
#include "video_core/renderer_opengl/gl_state.h"
|
||||||
|
#include "video_core/renderer_opengl/gl_vars.h"
|
||||||
|
|
||||||
namespace OpenGL {
|
namespace OpenGL {
|
||||||
|
|
||||||
@ -74,26 +75,26 @@ void TextureRuntime::ReadTexture(OGLTexture& texture, const BufferTextureCopy& c
|
|||||||
tuple.format, tuple.type, pixels.data() + copy.buffer_offset);
|
tuple.format, tuple.type, pixels.data() + copy.buffer_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TextureRuntime::ClearTexture(OGLTexture& texture, const ClearRect& rect, ClearValue value) {
|
bool TextureRuntime::ClearTexture(OGLTexture& texture, const TextureClear& clear, ClearValue value) {
|
||||||
OpenGLState prev_state = OpenGLState::GetCurState();
|
OpenGLState prev_state = OpenGLState::GetCurState();
|
||||||
SCOPE_EXIT({ prev_state.Apply(); });
|
SCOPE_EXIT({ prev_state.Apply(); });
|
||||||
|
|
||||||
// Setup scissor rectangle according to the clear rectangle
|
// Setup scissor rectangle according to the clear rectangle
|
||||||
OpenGLState state{};
|
OpenGLState state{};
|
||||||
state.scissor.enabled = true;
|
state.scissor.enabled = true;
|
||||||
state.scissor.x = rect.rect.offset.x;
|
state.scissor.x = clear.rect.offset.x;
|
||||||
state.scissor.y = rect.rect.offset.y;
|
state.scissor.y = clear.rect.offset.y;
|
||||||
state.scissor.width = rect.rect.extent.width;
|
state.scissor.width = clear.rect.extent.width;
|
||||||
state.scissor.height = rect.rect.extent.height;
|
state.scissor.height = clear.rect.extent.height;
|
||||||
state.draw.draw_framebuffer = draw_fbo.handle;
|
state.draw.draw_framebuffer = draw_fbo.handle;
|
||||||
state.Apply();
|
state.Apply();
|
||||||
|
|
||||||
switch (rect.surface_type) {
|
switch (clear.surface_type) {
|
||||||
case SurfaceType::Color:
|
case SurfaceType::Color:
|
||||||
case SurfaceType::Texture:
|
case SurfaceType::Texture:
|
||||||
case SurfaceType::Fill:
|
case SurfaceType::Fill:
|
||||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.handle,
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.handle,
|
||||||
rect.texture_level);
|
clear.texture_level);
|
||||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
|
||||||
0);
|
0);
|
||||||
|
|
||||||
@ -108,7 +109,7 @@ bool TextureRuntime::ClearTexture(OGLTexture& texture, const ClearRect& rect, Cl
|
|||||||
case SurfaceType::Depth:
|
case SurfaceType::Depth:
|
||||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, texture.handle,
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, texture.handle,
|
||||||
rect.texture_level);
|
clear.texture_level);
|
||||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
|
||||||
|
|
||||||
state.depth.write_mask = GL_TRUE;
|
state.depth.write_mask = GL_TRUE;
|
||||||
@ -119,7 +120,7 @@ bool TextureRuntime::ClearTexture(OGLTexture& texture, const ClearRect& rect, Cl
|
|||||||
case SurfaceType::DepthStencil:
|
case SurfaceType::DepthStencil:
|
||||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
|
||||||
texture.handle, rect.texture_level);
|
texture.handle, clear.texture_level);
|
||||||
|
|
||||||
state.depth.write_mask = GL_TRUE;
|
state.depth.write_mask = GL_TRUE;
|
||||||
state.stencil.write_mask = -1;
|
state.stencil.write_mask = -1;
|
||||||
@ -205,4 +206,43 @@ void TextureRuntime::GenerateMipmaps(OGLTexture& texture, u32 max_level) {
|
|||||||
glGenerateMipmap(GL_TEXTURE_2D);
|
glGenerateMipmap(GL_TEXTURE_2D);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const StagingBuffer& TextureRuntime::FindStaging(u32 size, bool upload) {
|
||||||
|
const GLbitfield access = upload ? GL_MAP_WRITE_BIT : GL_MAP_READ_BIT;
|
||||||
|
auto& search = upload ? upload_buffers : download_buffers;
|
||||||
|
|
||||||
|
const StagingBuffer key = {
|
||||||
|
.size = size
|
||||||
|
};
|
||||||
|
|
||||||
|
if (auto it = search.lower_bound(key); it != search.end()) {
|
||||||
|
return *it;
|
||||||
|
}
|
||||||
|
|
||||||
|
StagingBuffer staging{};
|
||||||
|
staging.buffer.Create();
|
||||||
|
|
||||||
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, staging.buffer.handle);
|
||||||
|
|
||||||
|
// Allocate a new buffer and map the data to the host
|
||||||
|
void* data = nullptr;
|
||||||
|
if (GLES) {
|
||||||
|
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);
|
||||||
|
} else {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
return *it;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace OpenGL
|
} // namespace OpenGL
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <span>
|
#include <span>
|
||||||
|
#include <set>
|
||||||
#include "video_core/rasterizer_cache/types.h"
|
#include "video_core/rasterizer_cache/types.h"
|
||||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||||
|
|
||||||
@ -11,6 +12,16 @@ namespace OpenGL {
|
|||||||
|
|
||||||
struct FormatTuple;
|
struct FormatTuple;
|
||||||
|
|
||||||
|
struct StagingBuffer {
|
||||||
|
u32 size = 0;
|
||||||
|
std::span<std::byte> mapped{};
|
||||||
|
OGLBuffer buffer{};
|
||||||
|
|
||||||
|
bool operator<(const StagingBuffer& other) const {
|
||||||
|
return size < other.size;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides texture manipulation functions to the rasterizer cache
|
* Provides texture manipulation functions to the rasterizer cache
|
||||||
* Separating this into a class makes it easier to abstract graphics API code
|
* Separating this into a class makes it easier to abstract graphics API code
|
||||||
@ -25,7 +36,7 @@ public:
|
|||||||
PixelFormat format, std::span<std::byte> pixels);
|
PixelFormat format, std::span<std::byte> pixels);
|
||||||
|
|
||||||
/// Fills the rectangle of the texture with the clear value provided
|
/// Fills the rectangle of the texture with the clear value provided
|
||||||
bool ClearTexture(OGLTexture& texture, const ClearRect& rect, ClearValue value);
|
bool ClearTexture(OGLTexture& texture, const TextureClear& clear, ClearValue value);
|
||||||
|
|
||||||
/// Copies a rectangle of src_tex to another rectange of dst_rect
|
/// Copies a rectangle of src_tex to another rectange of dst_rect
|
||||||
bool CopyTextures(OGLTexture& source, OGLTexture& dest, const TextureCopy& copy);
|
bool CopyTextures(OGLTexture& source, OGLTexture& dest, const TextureCopy& copy);
|
||||||
@ -36,8 +47,13 @@ public:
|
|||||||
/// Generates mipmaps for all the available levels of the texture
|
/// Generates mipmaps for all the available levels of the texture
|
||||||
void GenerateMipmaps(OGLTexture& texture, u32 max_level);
|
void GenerateMipmaps(OGLTexture& texture, u32 max_level);
|
||||||
|
|
||||||
|
/// Maps an internal staging buffer of the provided size of pixel uploads/downloads
|
||||||
|
const StagingBuffer& FindStaging(u32 size, bool upload);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
OGLFramebuffer read_fbo, draw_fbo;
|
OGLFramebuffer read_fbo, draw_fbo;
|
||||||
|
std::set<StagingBuffer> upload_buffers;
|
||||||
|
std::set<StagingBuffer> download_buffers;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace OpenGL
|
} // namespace OpenGL
|
||||||
|
@ -45,8 +45,9 @@ union ClearValue {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ClearRect {
|
struct TextureClear {
|
||||||
SurfaceType surface_type;
|
SurfaceType surface_type;
|
||||||
|
PixelFormat texture_format;
|
||||||
u32 texture_level;
|
u32 texture_level;
|
||||||
Rect2D rect;
|
Rect2D rect;
|
||||||
};
|
};
|
||||||
|
@ -53,7 +53,7 @@ void FrameDumperOpenGL::PresentLoop() {
|
|||||||
mailbox->ReloadPresentFrame(frame, layout.width, layout.height);
|
mailbox->ReloadPresentFrame(frame, layout.width, layout.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
frame->render_fence.Wait();
|
glWaitSync(frame->render_fence.handle, 0, GL_TIMEOUT_IGNORED);
|
||||||
|
|
||||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, frame->present.handle);
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, frame->present.handle);
|
||||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbos[current_pbo].handle);
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbos[current_pbo].handle);
|
||||||
|
@ -215,22 +215,6 @@ void OGLSync::Release() {
|
|||||||
handle = 0;
|
handle = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OGLSync::WaitHost() {
|
|
||||||
if (!handle) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
glClientWaitSync(handle, 0, GL_TIMEOUT_IGNORED);
|
|
||||||
}
|
|
||||||
|
|
||||||
void OGLSync::Wait() {
|
|
||||||
if (!handle) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
glWaitSync(handle, 0, GL_TIMEOUT_IGNORED);
|
|
||||||
}
|
|
||||||
|
|
||||||
void OGLVertexArray::Create() {
|
void OGLVertexArray::Create() {
|
||||||
if (handle != 0)
|
if (handle != 0)
|
||||||
return;
|
return;
|
||||||
|
@ -164,12 +164,6 @@ public:
|
|||||||
/// Deletes the internal OpenGL resource
|
/// Deletes the internal OpenGL resource
|
||||||
void Release();
|
void Release();
|
||||||
|
|
||||||
/// Causes the host to wait for the OpenGL semaphore
|
|
||||||
void WaitHost();
|
|
||||||
|
|
||||||
/// Causes the GPU to wait for the OpenGL semaphore
|
|
||||||
void Wait();
|
|
||||||
|
|
||||||
GLsync handle = 0;
|
GLsync handle = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -499,14 +499,18 @@ void RendererOpenGL::RenderToMailbox(const Layout::FramebufferLayout& layout,
|
|||||||
|
|
||||||
// INTEL driver workaround. We can't delete the previous render sync object until we are
|
// INTEL driver workaround. We can't delete the previous render sync object until we are
|
||||||
// sure that the presentation is done
|
// sure that the presentation is done
|
||||||
frame->present_fence.WaitHost();
|
if (frame->present_fence.handle) {
|
||||||
|
glClientWaitSync(frame->present_fence.handle, 0, GL_TIMEOUT_IGNORED);
|
||||||
|
}
|
||||||
|
|
||||||
// Delete the draw fence if the frame wasn't presented
|
// delete the draw fence if the frame wasn't presented
|
||||||
frame->render_fence.Release();
|
frame->render_fence.Release();
|
||||||
|
|
||||||
// wait for the presentation to be done
|
// wait for the presentation to be done
|
||||||
frame->present_fence.Wait();
|
if (frame->present_fence.handle) {
|
||||||
frame->present_fence.Release();
|
glWaitSync(frame->present_fence.handle, 0, GL_TIMEOUT_IGNORED);
|
||||||
|
frame->present_fence.Release();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -1145,7 +1149,7 @@ void RendererOpenGL::TryPresent(int timeout_ms, bool is_secondary) {
|
|||||||
window.mailbox->ReloadPresentFrame(frame, layout.width, layout.height);
|
window.mailbox->ReloadPresentFrame(frame, layout.width, layout.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
frame->render_fence.Wait();
|
glWaitSync(frame->render_fence.handle, 0, GL_TIMEOUT_IGNORED);
|
||||||
|
|
||||||
// INTEL workaround.
|
// INTEL workaround.
|
||||||
// Normally we could just delete the draw fence here, but due to driver bugs, we can just delete
|
// Normally we could just delete the draw fence here, but due to driver bugs, we can just delete
|
||||||
@ -1256,10 +1260,8 @@ VideoCore::ResultStatus RendererOpenGL::Init() {
|
|||||||
|
|
||||||
// Qualcomm has some spammy info messages that are marked as errors but not important
|
// Qualcomm has some spammy info messages that are marked as errors but not important
|
||||||
// https://developer.qualcomm.com/comment/11845
|
// https://developer.qualcomm.com/comment/11845
|
||||||
if (GLAD_GL_KHR_debug) {
|
glEnable(GL_DEBUG_OUTPUT);
|
||||||
glEnable(GL_DEBUG_OUTPUT);
|
glDebugMessageCallback(DebugHandler, nullptr);
|
||||||
glDebugMessageCallback(DebugHandler, nullptr);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const std::string_view gl_version{reinterpret_cast<char const*>(glGetString(GL_VERSION))};
|
const std::string_view gl_version{reinterpret_cast<char const*>(glGetString(GL_VERSION))};
|
||||||
@ -1272,20 +1274,24 @@ VideoCore::ResultStatus RendererOpenGL::Init() {
|
|||||||
|
|
||||||
auto& telemetry_session = Core::System::GetInstance().TelemetrySession();
|
auto& telemetry_session = Core::System::GetInstance().TelemetrySession();
|
||||||
constexpr auto user_system = Common::Telemetry::FieldType::UserSystem;
|
constexpr auto user_system = Common::Telemetry::FieldType::UserSystem;
|
||||||
telemetry_session.AddField(user_system, "GPU_Vendor", std::string(gpu_vendor));
|
telemetry_session.AddField(user_system, "GPU_Vendor", std::string{gpu_vendor});
|
||||||
telemetry_session.AddField(user_system, "GPU_Model", std::string(gpu_model));
|
telemetry_session.AddField(user_system, "GPU_Model", std::string{gpu_model});
|
||||||
telemetry_session.AddField(user_system, "GPU_OpenGL_Version", std::string(gl_version));
|
telemetry_session.AddField(user_system, "GPU_OpenGL_Version", std::string{gl_version});
|
||||||
|
|
||||||
if (gpu_vendor == "GDI Generic") {
|
if (gpu_vendor == "GDI Generic") {
|
||||||
return VideoCore::ResultStatus::ErrorGenericDrivers;
|
return VideoCore::ResultStatus::ErrorGenericDrivers;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(GLAD_GL_VERSION_4_3 || GLAD_GL_ES_VERSION_3_1)) {
|
if (!(GLAD_GL_VERSION_4_4 || GLAD_GL_ES_VERSION_3_2)) {
|
||||||
|
return VideoCore::ResultStatus::ErrorRendererInit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We require glBufferStorage(EXT)
|
||||||
|
if (GLES && !GLAD_GL_EXT_buffer_storage) {
|
||||||
return VideoCore::ResultStatus::ErrorRendererInit;
|
return VideoCore::ResultStatus::ErrorRendererInit;
|
||||||
}
|
}
|
||||||
|
|
||||||
InitOpenGLObjects();
|
InitOpenGLObjects();
|
||||||
|
|
||||||
RefreshRasterizerSetting();
|
RefreshRasterizerSetting();
|
||||||
|
|
||||||
return VideoCore::ResultStatus::Success;
|
return VideoCore::ResultStatus::Success;
|
||||||
|
@ -39,8 +39,8 @@ void TextureDownloaderES::Test() {
|
|||||||
}
|
}
|
||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
|
||||||
const auto test = [this, &state](FormatTuple tuple, auto original_data, std::size_t tex_size,
|
const auto test = [this, &state]<typename T>(FormatTuple tuple, std::vector<T> original_data,
|
||||||
auto data_generator) {
|
std::size_t tex_size, auto data_generator) {
|
||||||
OGLTexture texture;
|
OGLTexture texture;
|
||||||
texture.Create();
|
texture.Create();
|
||||||
state.texture_units[0].texture_2d = texture.handle;
|
state.texture_units[0].texture_2d = texture.handle;
|
||||||
@ -55,7 +55,7 @@ void TextureDownloaderES::Test() {
|
|||||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, tex_sizei, tex_sizei, tuple.format, tuple.type,
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, tex_sizei, tex_sizei, tuple.format, tuple.type,
|
||||||
original_data.data());
|
original_data.data());
|
||||||
|
|
||||||
decltype(original_data) new_data(original_data.size());
|
std::vector<T> new_data(original_data.size());
|
||||||
glFinish();
|
glFinish();
|
||||||
auto start = std::chrono::high_resolution_clock::now();
|
auto start = std::chrono::high_resolution_clock::now();
|
||||||
GetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, tex_sizei, tex_sizei,
|
GetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, tex_sizei, tex_sizei,
|
||||||
@ -131,7 +131,7 @@ void main(){
|
|||||||
state.draw.draw_framebuffer = depth32_fbo.handle;
|
state.draw.draw_framebuffer = depth32_fbo.handle;
|
||||||
state.renderbuffer = r32ui_renderbuffer.handle;
|
state.renderbuffer = r32ui_renderbuffer.handle;
|
||||||
state.Apply();
|
state.Apply();
|
||||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_R32UI, max_size, max_size);
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_R32UI, MAX_SIZE, MAX_SIZE);
|
||||||
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
|
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
|
||||||
r32ui_renderbuffer.handle);
|
r32ui_renderbuffer.handle);
|
||||||
glUniform1i(glGetUniformLocation(d24s8_r32ui_conversion_shader.program.handle, "depth"), 1);
|
glUniform1i(glGetUniformLocation(d24s8_r32ui_conversion_shader.program.handle, "depth"), 1);
|
||||||
@ -139,7 +139,7 @@ void main(){
|
|||||||
state.draw.draw_framebuffer = depth16_fbo.handle;
|
state.draw.draw_framebuffer = depth16_fbo.handle;
|
||||||
state.renderbuffer = r16_renderbuffer.handle;
|
state.renderbuffer = r16_renderbuffer.handle;
|
||||||
state.Apply();
|
state.Apply();
|
||||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_R16, max_size, max_size);
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_R16, MAX_SIZE, MAX_SIZE);
|
||||||
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
|
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
|
||||||
r16_renderbuffer.handle);
|
r16_renderbuffer.handle);
|
||||||
|
|
||||||
@ -152,7 +152,7 @@ void main(){
|
|||||||
*/
|
*/
|
||||||
GLuint TextureDownloaderES::ConvertDepthToColor(GLuint level, GLenum& format, GLenum& type,
|
GLuint TextureDownloaderES::ConvertDepthToColor(GLuint level, GLenum& format, GLenum& type,
|
||||||
GLint height, GLint width) {
|
GLint height, GLint width) {
|
||||||
ASSERT(width <= max_size && height <= max_size);
|
ASSERT(width <= MAX_SIZE && height <= MAX_SIZE);
|
||||||
const OpenGLState cur_state = OpenGLState::GetCurState();
|
const OpenGLState cur_state = OpenGLState::GetCurState();
|
||||||
OpenGLState state;
|
OpenGLState state;
|
||||||
state.texture_units[0] = {cur_state.texture_units[0].texture_2d, sampler.handle};
|
state.texture_units[0] = {cur_state.texture_units[0].texture_2d, sampler.handle};
|
||||||
|
@ -11,26 +11,33 @@ namespace OpenGL {
|
|||||||
class OpenGLState;
|
class OpenGLState;
|
||||||
|
|
||||||
class TextureDownloaderES {
|
class TextureDownloaderES {
|
||||||
static constexpr u16 max_size = 1024;
|
public:
|
||||||
|
TextureDownloaderES(bool enable_depth_stencil);
|
||||||
|
|
||||||
|
void GetTexImage(GLenum target, GLuint level, GLenum format, const GLenum type,
|
||||||
|
GLint height, GLint width, void* pixels);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Test();
|
||||||
|
GLuint ConvertDepthToColor(GLuint level, GLenum& format, GLenum& type,
|
||||||
|
GLint height, GLint width);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr u16 MAX_SIZE = 1024;
|
||||||
|
|
||||||
|
struct ConversionShader {
|
||||||
|
OGLProgram program;
|
||||||
|
GLint lod_location{-1};
|
||||||
|
};
|
||||||
|
|
||||||
OGLVertexArray vao;
|
OGLVertexArray vao;
|
||||||
OGLFramebuffer read_fbo_generic;
|
OGLFramebuffer read_fbo_generic;
|
||||||
OGLFramebuffer depth32_fbo, depth16_fbo;
|
OGLFramebuffer depth32_fbo, depth16_fbo;
|
||||||
OGLRenderbuffer r32ui_renderbuffer, r16_renderbuffer;
|
OGLRenderbuffer r32ui_renderbuffer, r16_renderbuffer;
|
||||||
struct ConversionShader {
|
|
||||||
OGLProgram program;
|
ConversionShader d24_r32ui_conversion_shader;
|
||||||
GLint lod_location{-1};
|
ConversionShader d16_r16_conversion_shader;
|
||||||
} d24_r32ui_conversion_shader, d16_r16_conversion_shader, d24s8_r32ui_conversion_shader;
|
ConversionShader d24s8_r32ui_conversion_shader;
|
||||||
OGLSampler sampler;
|
OGLSampler sampler;
|
||||||
|
|
||||||
void Test();
|
|
||||||
GLuint ConvertDepthToColor(GLuint level, GLenum& format, GLenum& type, GLint height,
|
|
||||||
GLint width);
|
|
||||||
|
|
||||||
public:
|
|
||||||
TextureDownloaderES(bool enable_depth_stencil);
|
|
||||||
|
|
||||||
void GetTexImage(GLenum target, GLuint level, GLenum format, const GLenum type, GLint height,
|
|
||||||
GLint width, void* pixels);
|
|
||||||
};
|
};
|
||||||
} // namespace OpenGL
|
} // namespace OpenGL
|
||||||
|
Reference in New Issue
Block a user