gl_texture_runtime: Clean up texture upload/download code

* Improve readability and code clarity
This commit is contained in:
emufan4568
2022-09-13 18:08:06 +03:00
committed by GPUCode
parent a932a9f662
commit d2fd8030dd
9 changed files with 295 additions and 300 deletions

View File

@ -22,6 +22,7 @@ struct Rectangle {
: left(left), top(top), right(right), bottom(bottom) {} : left(left), top(top), right(right), bottom(bottom) {}
constexpr auto operator<=>(const Rectangle&) const = default; constexpr auto operator<=>(const Rectangle&) const = default;
constexpr void operator*=(const T value) { constexpr void operator*=(const T value) {
left *= value; left *= value;
top *= value; top *= value;
@ -29,6 +30,9 @@ struct Rectangle {
bottom *= value; bottom *= value;
} }
[[nodiscard]] constexpr Rectangle operator*(const T value) const {
return Rectangle{left * value, top * value, right * value, bottom * value};
}
[[nodiscard]] constexpr T GetWidth() const { [[nodiscard]] constexpr T GetWidth() const {
return std::abs(static_cast<std::make_signed_t<T>>(right - left)); return std::abs(static_cast<std::make_signed_t<T>>(right - left));
} }

View File

@ -3,6 +3,7 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#pragma once #pragma once
#include <algorithm>
#include <unordered_map> #include <unordered_map>
#include <optional> #include <optional>
#include <boost/range/iterator_range.hpp> #include <boost/range/iterator_range.hpp>
@ -46,11 +47,11 @@ template <class T>
class RasterizerCache : NonCopyable { class RasterizerCache : NonCopyable {
public: public:
using TextureRuntime = typename T::Runtime; using TextureRuntime = typename T::Runtime;
using CachedSurface = typename T::Surface; using Surface = std::shared_ptr<typename T::Surface>;
using Watcher = SurfaceWatcher<typename T::Surface>;
private:
/// Declare rasterizer interval types /// Declare rasterizer interval types
using Surface = std::shared_ptr<CachedSurface>;
using Watcher = SurfaceWatcher<CachedSurface>;
using SurfaceSet = std::set<Surface>; using SurfaceSet = std::set<Surface>;
using SurfaceMap = using SurfaceMap =
boost::icl::interval_map<PAddr, Surface, boost::icl::partial_absorber, std::less, boost::icl::interval_map<PAddr, Surface, boost::icl::partial_absorber, std::less,
@ -131,6 +132,9 @@ private:
/// Copies pixel data in interval from the host GPU surface to the guest VRAM /// Copies pixel data in interval from the host GPU surface to the guest VRAM
void DownloadSurface(const Surface& surface, SurfaceInterval interval); void DownloadSurface(const Surface& surface, SurfaceInterval interval);
/// Downloads a fill surface to guest VRAM
void DownloadFillSurface(const Surface& surface, SurfaceInterval interval);
/// Returns false if there is a surface in the cache at the interval with the same bit-width, /// Returns false if there is a surface in the cache at the interval with the same bit-width,
bool NoUnimplementedReinterpretations(const Surface& surface, SurfaceParams& params, bool NoUnimplementedReinterpretations(const Surface& surface, SurfaceParams& params,
SurfaceInterval interval); SurfaceInterval interval);
@ -268,7 +272,6 @@ bool RasterizerCache<T>::BlitSurfaces(const Surface& src_surface, Common::Rectan
dst_surface->InvalidateAllWatcher(); dst_surface->InvalidateAllWatcher();
const TextureBlit texture_blit = { const TextureBlit texture_blit = {
.surface_type = src_surface->type,
.src_level = 0, .src_level = 0,
.dst_level = 0, .dst_level = 0,
.src_layer = 0, .src_layer = 0,
@ -277,7 +280,7 @@ bool RasterizerCache<T>::BlitSurfaces(const Surface& src_surface, Common::Rectan
.dst_rect = dst_rect .dst_rect = dst_rect
}; };
return runtime.BlitTextures(src_surface->texture, dst_surface->texture, texture_blit); return runtime.BlitTextures(*src_surface, *dst_surface, texture_blit);
} }
return false; return false;
@ -305,21 +308,17 @@ void RasterizerCache<T>::CopySurface(const Surface& src_surface, const Surface&
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 TextureClear clear_rect = { const TextureClear clear_rect = {
.surface_type = dst_surface->type,
.texture_format = dst_surface->pixel_format,
.texture_level = 0, .texture_level = 0,
.texture_rect = dst_surface->GetScaledSubRect(subrect_params) .texture_rect = dst_surface->GetScaledSubRect(subrect_params)
}; };
runtime.ClearTexture(dst_surface->texture, clear_rect, clear_value); runtime.ClearTexture(*dst_surface, clear_rect, clear_value);
return; return;
} }
if (src_surface->CanSubRect(subrect_params)) { if (src_surface->CanSubRect(subrect_params)) {
const TextureBlit texture_blit = { const TextureBlit texture_blit = {
.surface_type = src_surface->type,
.src_level = 0, .src_level = 0,
.dst_level = 0, .dst_level = 0,
.src_layer = 0, .src_layer = 0,
@ -328,7 +327,7 @@ void RasterizerCache<T>::CopySurface(const Surface& src_surface, const Surface&
.dst_rect = dst_surface->GetScaledSubRect(subrect_params) .dst_rect = dst_surface->GetScaledSubRect(subrect_params)
}; };
runtime.BlitTextures(src_surface->texture, dst_surface->texture, texture_blit); runtime.BlitTextures(*src_surface, *dst_surface, texture_blit);
return; return;
} }
@ -551,7 +550,6 @@ auto RasterizerCache<T>::GetTextureSurface(const Pica::Texture::TextureInfo& inf
if (/*texture_filterer->IsNull()*/true) { if (/*texture_filterer->IsNull()*/true) {
const TextureBlit texture_blit = { const TextureBlit texture_blit = {
.surface_type = surface->type,
.src_level = 0, .src_level = 0,
.dst_level = level, .dst_level = level,
.src_layer = 0, .src_layer = 0,
@ -560,7 +558,7 @@ auto RasterizerCache<T>::GetTextureSurface(const Pica::Texture::TextureInfo& inf
.dst_rect = surface_params.GetScaledRect() .dst_rect = surface_params.GetScaledRect()
}; };
runtime.BlitTextures(level_surface->texture, surface->texture, texture_blit); runtime.BlitTextures(*level_surface, *surface, texture_blit);
} }
watcher->Validate(); watcher->Validate();
@ -574,6 +572,7 @@ auto RasterizerCache<T>::GetTextureSurface(const Pica::Texture::TextureInfo& inf
template <class T> template <class T>
auto RasterizerCache<T>::GetTextureCube(const TextureCubeConfig& config) -> const Surface& { auto RasterizerCache<T>::GetTextureCube(const TextureCubeConfig& config) -> const Surface& {
auto& cube = texture_cube_cache[config]; auto& cube = texture_cube_cache[config];
cube->is_texture_cube = true;
struct Face { struct Face {
Face(std::shared_ptr<Watcher>& watcher, PAddr address) Face(std::shared_ptr<Watcher>& watcher, PAddr address)
@ -633,7 +632,6 @@ auto RasterizerCache<T>::GetTextureCube(const TextureCubeConfig& config) -> cons
} }
const TextureBlit texture_blit = { const TextureBlit texture_blit = {
.surface_type = SurfaceType::Color,
.src_level = 0, .src_level = 0,
.dst_level = 0, .dst_level = 0,
.src_layer = 0, .src_layer = 0,
@ -642,7 +640,7 @@ auto RasterizerCache<T>::GetTextureCube(const TextureCubeConfig& config) -> cons
.dst_rect = Rect2D{0, scaled_size, scaled_size, 0} .dst_rect = Rect2D{0, scaled_size, scaled_size, 0}
}; };
runtime.BlitTextures(surface->texture, cube->texture, texture_blit); runtime.BlitTextures(*surface, *cube, texture_blit);
face.watcher->Validate(); face.watcher->Validate();
} }
} }
@ -758,7 +756,7 @@ auto RasterizerCache<T>::GetFillSurface(const GPU::Regs::MemoryFillConfig& confi
params.type = SurfaceType::Fill; params.type = SurfaceType::Fill;
params.res_scale = std::numeric_limits<u16>::max(); params.res_scale = std::numeric_limits<u16>::max();
Surface new_surface = std::make_shared<CachedSurface>(params, runtime); Surface new_surface = std::make_shared<typename T::Surface>(params, runtime);
std::memcpy(&new_surface->fill_data[0], &config.value_32bit, 4); std::memcpy(&new_surface->fill_data[0], &config.value_32bit, 4);
if (config.fill_32bit) { if (config.fill_32bit) {
@ -903,8 +901,8 @@ void RasterizerCache<T>::UploadSurface(const Surface& surface, SurfaceInterval i
return; return;
} }
const u32 start_offset = load_start - surface->addr;
const auto upload_data = source_ptr.GetWriteBytes(load_end - load_start); const auto upload_data = source_ptr.GetWriteBytes(load_end - load_start);
const u32 start_offset = load_start - surface->addr;
const u32 upload_size = static_cast<u32>(upload_data.size()); const u32 upload_size = static_cast<u32>(upload_data.size());
MICROPROFILE_SCOPE(RasterizerCache_SurfaceLoad); MICROPROFILE_SCOPE(RasterizerCache_SurfaceLoad);
@ -925,7 +923,14 @@ void RasterizerCache<T>::UploadSurface(const Surface& surface, SurfaceInterval i
UnswizzleTexture(*surface, start_offset, upload_data, staging.mapped); UnswizzleTexture(*surface, start_offset, upload_data, staging.mapped);
} }
surface->UploadTexture(surface->GetSubRect(info), staging); const BufferTextureCopy upload = {
.buffer_offset = 0,
.buffer_size = staging.size,
.texture_rect = surface->GetSubRect(info),
.texture_level = 0
};
surface->Upload(upload, staging);
} }
MICROPROFILE_DECLARE(RasterizerCache_SurfaceFlush); MICROPROFILE_DECLARE(RasterizerCache_SurfaceFlush);
@ -937,38 +942,28 @@ void RasterizerCache<T>::DownloadSurface(const Surface& surface, SurfaceInterval
const auto& staging = runtime.FindStaging( const auto& staging = runtime.FindStaging(
surface->width * surface->height * GetBytesPerPixel(surface->pixel_format), false); surface->width * surface->height * GetBytesPerPixel(surface->pixel_format), false);
if (surface->type != SurfaceType::Fill) { const SurfaceParams params = surface->FromInterval(interval);
SurfaceParams params = surface->FromInterval(interval); const BufferTextureCopy download = {
surface->DownloadTexture(surface->GetSubRect(params), staging); .buffer_offset = 0,
} .buffer_size = staging.size,
.texture_rect = surface->GetSubRect(params),
.texture_level = 0
};
surface->Download(download, staging);
MemoryRef dest_ptr = VideoCore::g_memory->GetPhysicalRef(flush_start); MemoryRef dest_ptr = VideoCore::g_memory->GetPhysicalRef(flush_start);
if (!dest_ptr) [[unlikely]] { if (!dest_ptr) [[unlikely]] {
return; return;
} }
const auto start_offset = flush_start - surface->addr;
const auto download_dest = dest_ptr.GetWriteBytes(flush_end - flush_start); const auto download_dest = dest_ptr.GetWriteBytes(flush_end - flush_start);
const auto download_size = static_cast<u32>(download_dest.size()); const u32 start_offset = flush_start - surface->addr;
const u32 download_size = static_cast<u32>(download_dest.size());
MICROPROFILE_SCOPE(RasterizerCache_SurfaceFlush); MICROPROFILE_SCOPE(RasterizerCache_SurfaceFlush);
if (surface->type == SurfaceType::Fill) { if (!surface->is_tiled) {
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); ASSERT(surface->type == SurfaceType::Color);
const auto download_data = staging.mapped.subspan(start_offset, download_size); const auto download_data = staging.mapped.subspan(start_offset, download_size);
@ -985,16 +980,48 @@ void RasterizerCache<T>::DownloadSurface(const Surface& surface, SurfaceInterval
} }
} }
template <class T>
void RasterizerCache<T>::DownloadFillSurface(const Surface& surface, 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);
MemoryRef dest_ptr = VideoCore::g_memory->GetPhysicalRef(flush_start);
if (!dest_ptr) [[unlikely]] {
return;
}
const u32 start_offset = flush_start - surface->addr;
const u32 download_size = std::clamp(flush_end - flush_start, 0u, static_cast<u32>(dest_ptr.GetSize()));
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);
}
}
template <class T> template <class T>
bool RasterizerCache<T>::NoUnimplementedReinterpretations(const Surface& surface, SurfaceParams& params, bool RasterizerCache<T>::NoUnimplementedReinterpretations(const Surface& surface, SurfaceParams& params,
SurfaceInterval interval) { SurfaceInterval interval) {
static constexpr std::array<PixelFormat, 17> all_formats{ static constexpr std::array all_formats = {
PixelFormat::RGBA8, PixelFormat::RGB8, PixelFormat::RGB5A1, PixelFormat::RGB565, PixelFormat::RGBA8, PixelFormat::RGB8, PixelFormat::RGB5A1, PixelFormat::RGB565,
PixelFormat::RGBA4, PixelFormat::IA8, PixelFormat::RG8, PixelFormat::I8, PixelFormat::RGBA4, PixelFormat::IA8, PixelFormat::RG8, PixelFormat::I8,
PixelFormat::A8, PixelFormat::IA4, PixelFormat::I4, PixelFormat::A4, PixelFormat::A8, PixelFormat::IA4, PixelFormat::I4, PixelFormat::A4,
PixelFormat::ETC1, PixelFormat::ETC1A4, PixelFormat::D16, PixelFormat::D24, PixelFormat::ETC1, PixelFormat::ETC1A4, PixelFormat::D16, PixelFormat::D24,
PixelFormat::D24S8, PixelFormat::D24S8,
}; };
bool implemented = true; bool implemented = true;
for (PixelFormat format : all_formats) { for (PixelFormat format : all_formats) {
if (GetFormatBpp(format) == surface->GetFormatBpp()) { if (GetFormatBpp(format) == surface->GetFormatBpp()) {
@ -1017,13 +1044,16 @@ bool RasterizerCache<T>::NoUnimplementedReinterpretations(const Surface& surface
template <class T> template <class T>
bool RasterizerCache<T>::IntervalHasInvalidPixelFormat(SurfaceParams& params, SurfaceInterval interval) { bool RasterizerCache<T>::IntervalHasInvalidPixelFormat(SurfaceParams& params, SurfaceInterval interval) {
params.pixel_format = PixelFormat::Invalid; params.pixel_format = PixelFormat::Invalid;
for (const auto& set : RangeFromInterval(surface_cache, interval)) for (const auto& set : RangeFromInterval(surface_cache, interval)) {
for (const auto& surface : set.second) for (const auto& surface : set.second) {
if (surface->pixel_format == PixelFormat::Invalid) { if (surface->pixel_format == PixelFormat::Invalid) {
LOG_DEBUG(Render_OpenGL, "Surface {:#x} found with invalid pixel format", LOG_DEBUG(Render_OpenGL, "Surface {:#x} found with invalid pixel format",
surface->addr); surface->addr);
return true; return true;
} }
}
}
return false; return false;
} }
@ -1115,7 +1145,12 @@ void RasterizerCache<T>::FlushRegion(PAddr addr, u32 size, Surface flush_surface
// Sanity check, this surface is the last one that marked this region dirty // Sanity check, this surface is the last one that marked this region dirty
ASSERT(surface->IsRegionValid(interval)); ASSERT(surface->IsRegionValid(interval));
DownloadSurface(surface, interval); if (surface->type == SurfaceType::Fill) {
DownloadFillSurface(surface, interval);
} else {
DownloadSurface(surface, interval);
}
flushed_intervals += interval; flushed_intervals += interval;
} }
@ -1132,11 +1167,11 @@ template <class T>
void RasterizerCache<T>::InvalidateRegion(PAddr addr, u32 size, const Surface& region_owner) { void RasterizerCache<T>::InvalidateRegion(PAddr addr, u32 size, const Surface& region_owner) {
std::lock_guard lock{mutex}; std::lock_guard lock{mutex};
if (size == 0) if (size == 0) [[unlikely]] {
return; return;
}
const SurfaceInterval invalid_interval(addr, addr + size); const SurfaceInterval invalid_interval{addr, addr + size};
if (region_owner != nullptr) { if (region_owner != nullptr) {
ASSERT(region_owner->type != SurfaceType::Texture); ASSERT(region_owner->type != SurfaceType::Texture);
ASSERT(addr >= region_owner->addr && addr + size <= region_owner->end); ASSERT(addr >= region_owner->addr && addr + size <= region_owner->end);
@ -1195,7 +1230,7 @@ void RasterizerCache<T>::InvalidateRegion(PAddr addr, u32 size, const Surface& r
template <class T> template <class T>
auto RasterizerCache<T>::CreateSurface(SurfaceParams& params) -> Surface { auto RasterizerCache<T>::CreateSurface(SurfaceParams& params) -> Surface {
Surface surface = std::make_shared<CachedSurface>(params, runtime); Surface surface = std::make_shared<typename T::Surface>(params, runtime);
surface->invalid_regions.insert(surface->GetInterval()); surface->invalid_regions.insert(surface->GetInterval());
// Allocate surface texture // Allocate surface texture

View File

@ -3,9 +3,9 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#pragma once #pragma once
#include "common/common_types.h"
#include "common/math_util.h" #include "common/math_util.h"
#include "common/vector_math.h" #include "common/vector_math.h"
#include "video_core/rasterizer_cache/pixel_format.h"
namespace VideoCore { namespace VideoCore {
@ -34,14 +34,11 @@ union ClearValue {
}; };
struct TextureClear { struct TextureClear {
SurfaceType surface_type;
PixelFormat texture_format;
u32 texture_level; u32 texture_level;
Rect2D texture_rect; Rect2D texture_rect;
}; };
struct TextureCopy { struct TextureCopy {
SurfaceType surface_type;
u32 src_level; u32 src_level;
u32 dst_level; u32 dst_level;
Offset src_offset; Offset src_offset;
@ -50,7 +47,6 @@ struct TextureCopy {
}; };
struct TextureBlit { struct TextureBlit {
SurfaceType surface_type;
u32 src_level; u32 src_level;
u32 dst_level; u32 dst_level;
u32 src_layer; u32 src_layer;
@ -62,9 +58,6 @@ struct TextureBlit {
struct BufferTextureCopy { struct BufferTextureCopy {
u32 buffer_offset; u32 buffer_offset;
u32 buffer_size; u32 buffer_size;
u32 buffer_row_length;
u32 buffer_height;
SurfaceType surface_type;
Rect2D texture_rect; Rect2D texture_rect;
u32 texture_level; u32 texture_level;
}; };

View File

@ -52,7 +52,10 @@ GLbitfield MakeBufferMask(VideoCore::SurfaceType type) {
return GL_COLOR_BUFFER_BIT; return GL_COLOR_BUFFER_BIT;
} }
TextureRuntime::TextureRuntime(Driver& driver) : driver(driver) { TextureRuntime::TextureRuntime(Driver& driver)
: driver{driver}, downloader_es{false},
filterer{Settings::values.texture_filter_name, VideoCore::GetResolutionScaleFactor()} {
read_fbo.Create(); read_fbo.Create();
draw_fbo.Create(); draw_fbo.Create();
} }
@ -123,10 +126,10 @@ const FormatTuple& TextureRuntime::GetFormatTuple(VideoCore::PixelFormat pixel_f
OGLTexture TextureRuntime::Allocate2D(u32 width, u32 height, VideoCore::PixelFormat format) { OGLTexture TextureRuntime::Allocate2D(u32 width, u32 height, VideoCore::PixelFormat format) {
const auto& tuple = GetFormatTuple(format); const auto& tuple = GetFormatTuple(format);
auto recycled_tex = texture2d_recycler.find({format, width, height}); auto recycled_tex = texture_recycler.find({format, width, height});
if (recycled_tex != texture2d_recycler.end()) { if (recycled_tex != texture_recycler.end()) {
OGLTexture texture = std::move(recycled_tex->second); OGLTexture texture = std::move(recycled_tex->second);
texture2d_recycler.erase(recycled_tex); texture_recycler.erase(recycled_tex);
return texture; return texture;
} }
@ -151,48 +154,7 @@ OGLTexture TextureRuntime::AllocateCubeMap(u32 width, VideoCore::PixelFormat for
return texture; return texture;
} }
void TextureRuntime::ReadTexture(OGLTexture& texture, const VideoCore::BufferTextureCopy& copy, bool TextureRuntime::ClearTexture(Surface& surface, const VideoCore::TextureClear& clear,
VideoCore::PixelFormat format) {
OpenGLState prev_state = OpenGLState::GetCurState();
SCOPE_EXIT({ prev_state.Apply(); });
OpenGLState state{};
state.ResetTexture(texture.handle);
state.draw.read_framebuffer = read_fbo.handle;
state.Apply();
switch (copy.surface_type) {
case VideoCore::SurfaceType::Color:
case VideoCore::SurfaceType::Texture:
case VideoCore::SurfaceType::Fill:
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.handle,
copy.texture_level);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
0);
break;
case VideoCore::SurfaceType::Depth:
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, texture.handle,
copy.texture_level);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
break;
case VideoCore::SurfaceType::DepthStencil:
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
texture.handle, copy.texture_level);
break;
default:
UNREACHABLE_MSG("Invalid surface type!");
}
const FormatTuple& tuple = GetFormatTuple(format);
glReadPixels(copy.texture_rect.left, copy.texture_rect.bottom,
copy.texture_rect.GetWidth(), copy.texture_rect.GetHeight(),
tuple.format, tuple.type, reinterpret_cast<void*>(copy.buffer_offset));
}
bool TextureRuntime::ClearTexture(OGLTexture& texture, const VideoCore::TextureClear& clear,
VideoCore::ClearValue value) { VideoCore::ClearValue value) {
OpenGLState prev_state = OpenGLState::GetCurState(); OpenGLState prev_state = OpenGLState::GetCurState();
SCOPE_EXIT({ prev_state.Apply(); }); SCOPE_EXIT({ prev_state.Apply(); });
@ -207,11 +169,12 @@ bool TextureRuntime::ClearTexture(OGLTexture& texture, const VideoCore::TextureC
state.draw.draw_framebuffer = draw_fbo.handle; state.draw.draw_framebuffer = draw_fbo.handle;
state.Apply(); state.Apply();
switch (clear.surface_type) { GLint handle = surface.texture.handle;
switch (surface.type) {
case VideoCore::SurfaceType::Color: case VideoCore::SurfaceType::Color:
case VideoCore::SurfaceType::Texture: case VideoCore::SurfaceType::Texture:
case VideoCore::SurfaceType::Fill: case VideoCore::SurfaceType::Fill:
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.handle, glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, handle,
clear.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);
@ -226,7 +189,7 @@ bool TextureRuntime::ClearTexture(OGLTexture& texture, const VideoCore::TextureC
break; break;
case VideoCore::SurfaceType::Depth: case VideoCore::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, handle,
clear.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);
@ -238,7 +201,7 @@ bool TextureRuntime::ClearTexture(OGLTexture& texture, const VideoCore::TextureC
case VideoCore::SurfaceType::DepthStencil: case VideoCore::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, clear.texture_level); 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;
@ -253,11 +216,11 @@ bool TextureRuntime::ClearTexture(OGLTexture& texture, const VideoCore::TextureC
return true; return true;
} }
bool TextureRuntime::CopyTextures(OGLTexture& source, OGLTexture& dest, const VideoCore::TextureCopy& copy) { bool TextureRuntime::CopyTextures(Surface& source, Surface& dest, const VideoCore::TextureCopy& copy) {
return true; return true;
} }
bool TextureRuntime::BlitTextures(OGLTexture& source, OGLTexture& dest, const VideoCore::TextureBlit& blit) { bool TextureRuntime::BlitTextures(Surface& source, Surface& dest, const VideoCore::TextureBlit& blit) {
OpenGLState prev_state = OpenGLState::GetCurState(); OpenGLState prev_state = OpenGLState::GetCurState();
SCOPE_EXIT({ prev_state.Apply(); }); SCOPE_EXIT({ prev_state.Apply(); });
@ -266,45 +229,20 @@ bool TextureRuntime::BlitTextures(OGLTexture& source, OGLTexture& dest, const Vi
state.draw.draw_framebuffer = draw_fbo.handle; state.draw.draw_framebuffer = draw_fbo.handle;
state.Apply(); state.Apply();
auto BindAttachment = [&blit, &source, &dest](GLenum attachment, u32 src_tex, u32 dst_tex) -> void { const GLenum src_textarget = source.is_texture_cube ?
const GLenum src_target = source.target == GL_TEXTURE_CUBE_MAP ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + blit.src_layer : GL_TEXTURE_2D;
GL_TEXTURE_CUBE_MAP_POSITIVE_X + blit.src_layer : source.target; BindFramebuffer(GL_READ_FRAMEBUFFER, blit.src_level, src_textarget, source.type, source.texture);
const GLenum dst_target = dest.target == GL_TEXTURE_CUBE_MAP ?
GL_TEXTURE_CUBE_MAP_POSITIVE_X + blit.dst_layer : dest.target;
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, attachment, src_target, src_tex, blit.src_level); const GLenum dst_textarget = dest.is_texture_cube ?
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, dst_target, dst_tex, blit.dst_level); GL_TEXTURE_CUBE_MAP_POSITIVE_X + blit.dst_layer : GL_TEXTURE_2D;
}; BindFramebuffer(GL_DRAW_FRAMEBUFFER, blit.dst_level, dst_textarget, dest.type, dest.texture);
switch (blit.surface_type) {
case VideoCore::SurfaceType::Color:
case VideoCore::SurfaceType::Texture:
case VideoCore::SurfaceType::Fill:
// Bind only color
BindAttachment(GL_COLOR_ATTACHMENT0, source.handle, dest.handle);
BindAttachment(GL_DEPTH_STENCIL_ATTACHMENT, 0, 0);
break;
case VideoCore::SurfaceType::Depth:
// Bind only depth
BindAttachment(GL_COLOR_ATTACHMENT0, 0, 0);
BindAttachment(GL_DEPTH_ATTACHMENT, source.handle, dest.handle);
BindAttachment(GL_STENCIL_ATTACHMENT, 0, 0);
break;
case VideoCore::SurfaceType::DepthStencil:
// Bind to combined depth + stencil
BindAttachment(GL_COLOR_ATTACHMENT0, 0, 0);
BindAttachment(GL_DEPTH_STENCIL_ATTACHMENT, source.handle, dest.handle);
break;
default:
UNREACHABLE_MSG("Invalid surface type!");
}
// TODO (wwylele): use GL_NEAREST for shadow map texture // TODO (wwylele): use GL_NEAREST for shadow map texture
// Note: shadow map is treated as RGBA8 format in PICA, as well as in the rasterizer cache, but // Note: shadow map is treated as RGBA8 format in PICA, as well as in the rasterizer cache, but
// doing linear intepolation componentwise would cause incorrect value. However, for a // doing linear intepolation componentwise would cause incorrect value. However, for a
// well-programmed game this code path should be rarely executed for shadow map with // well-programmed game this code path should be rarely executed for shadow map with
// inconsistent scale. // inconsistent scale.
const GLbitfield buffer_mask = MakeBufferMask(blit.surface_type); const GLbitfield buffer_mask = MakeBufferMask(source.type);
const GLenum filter = buffer_mask == GL_COLOR_BUFFER_BIT ? GL_LINEAR : GL_NEAREST; const GLenum filter = buffer_mask == GL_COLOR_BUFFER_BIT ? GL_LINEAR : GL_NEAREST;
glBlitFramebuffer(blit.src_rect.left, blit.src_rect.bottom, blit.src_rect.right, blit.src_rect.top, glBlitFramebuffer(blit.src_rect.left, blit.src_rect.bottom, blit.src_rect.right, blit.src_rect.top,
blit.dst_rect.left, blit.dst_rect.bottom, blit.dst_rect.right, blit.dst_rect.top, blit.dst_rect.left, blit.dst_rect.bottom, blit.dst_rect.right, blit.dst_rect.top,
@ -313,166 +251,165 @@ bool TextureRuntime::BlitTextures(OGLTexture& source, OGLTexture& dest, const Vi
return true; return true;
} }
void TextureRuntime::GenerateMipmaps(OGLTexture& texture, u32 max_level) { void TextureRuntime::GenerateMipmaps(Surface& surface, u32 max_level) {
OpenGLState prev_state = OpenGLState::GetCurState(); OpenGLState prev_state = OpenGLState::GetCurState();
SCOPE_EXIT({ prev_state.Apply(); }); SCOPE_EXIT({ prev_state.Apply(); });
OpenGLState state{}; OpenGLState state{};
state.texture_units[0].texture_2d = texture.handle; state.texture_units[0].texture_2d = surface.texture.handle;
state.Apply(); state.Apply();
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, max_level); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, max_level);
glGenerateMipmap(GL_TEXTURE_2D); glGenerateMipmap(GL_TEXTURE_2D);
} }
void TextureRuntime::BindFramebuffer(GLenum target, GLint level, GLenum textarget,
VideoCore::SurfaceType type, OGLTexture& texture) const {
const GLint framebuffer = target == GL_DRAW_FRAMEBUFFER ? draw_fbo.handle : read_fbo.handle;
glBindFramebuffer(target, framebuffer);
switch (type) {
case VideoCore::SurfaceType::Color:
case VideoCore::SurfaceType::Texture:
glFramebufferTexture2D(target, GL_COLOR_ATTACHMENT0, textarget, texture.handle, level);
break;
case VideoCore::SurfaceType::Depth:
glFramebufferTexture2D(target, GL_DEPTH_ATTACHMENT, textarget, texture.handle, level);
break;
case VideoCore::SurfaceType::DepthStencil:
glFramebufferTexture2D(target, GL_DEPTH_STENCIL_ATTACHMENT, textarget, texture.handle, level);
break;
default:
UNREACHABLE_MSG("Invalid surface type!");
}
}
Surface::Surface(VideoCore::SurfaceParams& params, TextureRuntime& runtime)
: VideoCore::SurfaceBase<Surface>{params}, runtime{runtime}, driver{runtime.GetDriver()} {
}
MICROPROFILE_DEFINE(RasterizerCache_TextureUL, "RasterizerCache", "Texture Upload", MP_RGB(128, 192, 64)); MICROPROFILE_DEFINE(RasterizerCache_TextureUL, "RasterizerCache", "Texture Upload", MP_RGB(128, 192, 64));
void CachedSurface::UploadTexture(Common::Rectangle<u32> rect, const StagingBuffer& staging) { void Surface::Upload(const VideoCore::BufferTextureCopy& upload, const StagingBuffer& staging) {
MICROPROFILE_SCOPE(RasterizerCache_TextureUL); MICROPROFILE_SCOPE(RasterizerCache_TextureUL);
const FormatTuple& tuple = runtime.GetFormatTuple(pixel_format); // Ensure no bad interactions with GL_UNPACK_ALIGNMENT
ASSERT(stride * GetBytesPerPixel(pixel_format) % 4 == 0);
// Load data from memory to the surface
GLint x0 = static_cast<GLint>(rect.left);
GLint y0 = static_cast<GLint>(rect.bottom);
std::size_t buffer_offset = (y0 * stride + x0) * GetBytesPerPixel(pixel_format);
GLuint target_tex = texture.handle;
// If not 1x scale, create 1x texture that we will blit from to replace texture subrect in surface
OGLTexture unscaled_tex;
if (res_scale != 1) {
x0 = 0;
y0 = 0;
unscaled_tex = runtime.Allocate2D(rect.GetWidth(), rect.GetHeight(), pixel_format);
target_tex = unscaled_tex.handle;
}
OpenGLState prev_state = OpenGLState::GetCurState(); OpenGLState prev_state = OpenGLState::GetCurState();
SCOPE_EXIT({ prev_state.Apply(); }); SCOPE_EXIT({ prev_state.Apply(); });
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, target_tex);
// Ensure no bad interactions with GL_UNPACK_ALIGNMENT
ASSERT(stride * GetBytesPerPixel(pixel_format) % 4 == 0);
glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(stride)); glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(stride));
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, staging.buffer.handle); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, staging.buffer.handle);
glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast<GLsizei>(rect.GetWidth()), const bool is_scaled = res_scale != 1;
static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type, if (is_scaled) {
reinterpret_cast<void*>(buffer_offset)); ScaledUpload(upload);
} else {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture.handle);
// Lock the staging buffer until glTexSubImage completes const auto& tuple = runtime.GetFormatTuple(pixel_format);
staging.Lock(); glTexSubImage2D(GL_TEXTURE_2D, upload.texture_level,
upload.texture_rect.left, upload.texture_rect.bottom,
upload.texture_rect.GetWidth(),
upload.texture_rect.GetHeight(),
tuple.format, tuple.type,
reinterpret_cast<void*>(upload.buffer_offset));
}
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
if (res_scale != 1) { // Lock the staging buffer until glTexSubImage completes
auto scaled_rect = rect; staging.Lock();
scaled_rect.left *= res_scale;
scaled_rect.top *= res_scale;
scaled_rect.right *= res_scale;
scaled_rect.bottom *= res_scale;
const Common::Rectangle<u32> from_rect{0, rect.GetHeight(), rect.GetWidth(), 0};
/*if (!owner.texture_filterer->Filter(unscaled_tex, from_rect, texture, scaled_rect, type)) {
const TextureBlit texture_blit = {
.surface_type = type,
.src_level = 0,
.dst_level = 0,
.src_region = Region2D{
.start = {0, 0},
.end = {width, height}
},
.dst_region = Region2D{
.start = {rect.left, rect.bottom},
.end = {rect.right, rect.top}
}
};
runtime.BlitTextures(unscaled_tex, texture, texture_blit);
}*/
}
InvalidateAllWatcher(); InvalidateAllWatcher();
} }
MICROPROFILE_DEFINE(RasterizerCache_TextureDL, "RasterizerCache", "Texture Download", MP_RGB(128, 192, 64)); MICROPROFILE_DEFINE(RasterizerCache_TextureDL, "RasterizerCache", "Texture Download", MP_RGB(128, 192, 64));
void CachedSurface::DownloadTexture(Common::Rectangle<u32> rect, const StagingBuffer& staging) { void Surface::Download(const VideoCore::BufferTextureCopy& download, const StagingBuffer& staging) {
MICROPROFILE_SCOPE(RasterizerCache_TextureDL); MICROPROFILE_SCOPE(RasterizerCache_TextureDL);
const FormatTuple& tuple = runtime.GetFormatTuple(pixel_format);
const u32 buffer_offset = (rect.bottom * stride + rect.left) * GetBytesPerPixel(pixel_format);
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);
OpenGLState prev_state = OpenGLState::GetCurState();
SCOPE_EXIT({ prev_state.Apply(); });
glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(stride)); glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(stride));
glBindBuffer(GL_PIXEL_PACK_BUFFER, staging.buffer.handle); glBindBuffer(GL_PIXEL_PACK_BUFFER, staging.buffer.handle);
// If not 1x scale, blit scaled texture to a new 1x texture and use that to flush const bool is_scaled = res_scale != 1;
if (res_scale != 1) { if (is_scaled) {
auto scaled_rect = rect; ScaledDownload(download);
scaled_rect.left *= res_scale;
scaled_rect.top *= res_scale;
scaled_rect.right *= res_scale;
scaled_rect.bottom *= res_scale;
OGLTexture unscaled_tex = runtime.Allocate2D(rect.GetWidth(), rect.GetHeight(), pixel_format);
const VideoCore::TextureBlit texture_blit = {
.surface_type = type,
.src_level = 0,
.dst_level = 0,
.src_rect = scaled_rect,
.dst_rect = VideoCore::Rect2D{0, rect.GetHeight(), rect.GetWidth(), 0}
};
// Blit scaled texture to the unscaled one
runtime.BlitTextures(texture, unscaled_tex, texture_blit);
state.texture_units[0].texture_2d = unscaled_tex.handle;
state.Apply();
glActiveTexture(GL_TEXTURE0);
/*if (GLES) {
owner.texture_downloader_es->GetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type,
rect.GetHeight(), rect.GetWidth(),
reinterpret_cast<void*>(buffer_offset));
} else {
glGetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, reinterpret_cast<void*>(buffer_offset));
}*/
glGetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, reinterpret_cast<void*>(buffer_offset));
} else { } else {
const u32 download_size = width * height * GetBytesPerPixel(pixel_format); runtime.BindFramebuffer(GL_READ_FRAMEBUFFER, download.texture_level, GL_TEXTURE_2D, type, texture);
const VideoCore::BufferTextureCopy texture_download = {
.buffer_offset = buffer_offset,
.buffer_size = download_size,
.buffer_row_length = stride,
.buffer_height = height,
.surface_type = type,
.texture_rect = rect,
.texture_level = 0
};
runtime.ReadTexture(texture, texture_download, pixel_format); const auto& tuple = runtime.GetFormatTuple(pixel_format);
glReadPixels(download.texture_rect.left, download.texture_rect.bottom,
download.texture_rect.GetWidth(), download.texture_rect.GetHeight(),
tuple.format, tuple.type, reinterpret_cast<void*>(download.buffer_offset));
} }
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
glPixelStorei(GL_PACK_ROW_LENGTH, 0); glPixelStorei(GL_PACK_ROW_LENGTH, 0);
} }
void CachedSurface::Scale() { void Surface::ScaledDownload(const VideoCore::BufferTextureCopy& download) {
const u32 rect_width = download.texture_rect.GetWidth();
const u32 rect_height = download.texture_rect.GetHeight();
// Allocate an unscaled texture that fits the download rectangle to use as a blit destination
OGLTexture unscaled_tex = runtime.Allocate2D(rect_width, rect_height, pixel_format);
runtime.BindFramebuffer(GL_DRAW_FRAMEBUFFER, 0, GL_TEXTURE_2D, type, unscaled_tex);
runtime.BindFramebuffer(GL_READ_FRAMEBUFFER, download.texture_level, GL_TEXTURE_2D, type, texture);
// Blit the scaled rectangle to the unscaled texture
const VideoCore::Rect2D scaled_rect = download.texture_rect * res_scale;
glBlitFramebuffer(scaled_rect.left, scaled_rect.bottom, scaled_rect.right, scaled_rect.top,
0, 0, rect_width, rect_height, MakeBufferMask(type), GL_LINEAR);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, unscaled_tex.handle);
const auto& tuple = runtime.GetFormatTuple(pixel_format);
if (driver.IsOpenGLES()) {
const auto& downloader_es = runtime.GetDownloaderES();
downloader_es.GetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type,
rect_height, rect_width,
reinterpret_cast<void*>(download.buffer_offset));
} else {
glGetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type,
reinterpret_cast<void*>(download.buffer_offset));
}
}
void Surface::ScaledUpload(const VideoCore::BufferTextureCopy& upload) {
const u32 rect_width = upload.texture_rect.GetWidth();
const u32 rect_height = upload.texture_rect.GetHeight();
OGLTexture unscaled_tex = runtime.Allocate2D(rect_width, rect_height, pixel_format);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, unscaled_tex.handle);
const auto& tuple = runtime.GetFormatTuple(pixel_format);
glTexSubImage2D(GL_TEXTURE_2D, upload.texture_level, 0, 0, rect_width, rect_height,
tuple.format, tuple.type, reinterpret_cast<void*>(upload.buffer_offset));
const auto scaled_rect = upload.texture_rect * res_scale;
const auto unscaled_rect = VideoCore::Rect2D{0, rect_height, rect_width, 0};
const auto& filterer = runtime.GetFilterer();
if (!filterer.Filter(unscaled_tex, unscaled_rect, texture, scaled_rect, type)) {
runtime.BindFramebuffer(GL_READ_FRAMEBUFFER, 0, GL_TEXTURE_2D, type, unscaled_tex);
runtime.BindFramebuffer(GL_DRAW_FRAMEBUFFER, upload.texture_level, GL_TEXTURE_2D, type, texture);
// If filtering fails, resort to normal blitting
glBlitFramebuffer(0, 0, rect_width, rect_height,
upload.texture_rect.left, upload.texture_rect.bottom,
upload.texture_rect.right, upload.texture_rect.top,
MakeBufferMask(type), GL_LINEAR);
}
} }
} // namespace OpenGL } // namespace OpenGL

View File

@ -9,6 +9,8 @@
#include "video_core/rasterizer_cache/surface_base.h" #include "video_core/rasterizer_cache/surface_base.h"
#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"
#include "video_core/renderer_opengl/texture_filters/texture_filterer.h"
#include "video_core/renderer_opengl/texture_downloader_es.h"
namespace OpenGL { namespace OpenGL {
@ -50,13 +52,14 @@ struct StagingBuffer {
}; };
class Driver; class Driver;
class Surface;
/** /**
* 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
*/ */
class TextureRuntime { class TextureRuntime {
friend class CachedSurface; friend class Surface;
public: public:
TextureRuntime(Driver& driver); TextureRuntime(Driver& driver);
~TextureRuntime() = default; ~TextureRuntime() = default;
@ -73,51 +76,74 @@ public:
/// Allocates an OpenGL cube map texture with the specified dimentions and format /// Allocates an OpenGL cube map texture with the specified dimentions and format
OGLTexture AllocateCubeMap(u32 width, VideoCore::PixelFormat format); OGLTexture AllocateCubeMap(u32 width, VideoCore::PixelFormat format);
/// Copies the GPU pixel data to the provided pixels buffer
void ReadTexture(OGLTexture& texture, const VideoCore::BufferTextureCopy& copy,
VideoCore::PixelFormat format);
/// 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 VideoCore::TextureClear& clear, bool ClearTexture(Surface& surface, const VideoCore::TextureClear& clear,
VideoCore::ClearValue value); VideoCore::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 VideoCore::TextureCopy& copy); bool CopyTextures(Surface& source, Surface& dest, const VideoCore::TextureCopy& copy);
/// Blits a rectangle of src_tex to another rectange of dst_rect /// Blits a rectangle of src_tex to another rectange of dst_rect
bool BlitTextures(OGLTexture& source, OGLTexture& dest, const VideoCore::TextureBlit& blit); bool BlitTextures(Surface& surface, Surface& dest, const VideoCore::TextureBlit& blit);
/// 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(Surface& surface, u32 max_level);
private:
/// Returns the framebuffer used for texture downloads
void BindFramebuffer(GLenum target, GLint level, GLenum textarget,
VideoCore::SurfaceType type, OGLTexture& texture) const;
/// Returns the OpenGL driver class
const Driver& GetDriver() const {
return driver;
}
/// Returns the class that handles texture downloads for OpenGL ES
const TextureDownloaderES& GetDownloaderES() const {
return downloader_es;
}
/// Returns the class that handles texture filtering
const TextureFilterer& GetFilterer() const {
return filterer;
}
private: private:
Driver& driver; Driver& driver;
OGLFramebuffer read_fbo, draw_fbo; TextureDownloaderES downloader_es;
std::unordered_multimap<VideoCore::HostTextureTag, OGLTexture> texture2d_recycler; TextureFilterer filterer;
// Staging buffers stored in increasing size // Staging buffers stored in increasing size
std::multiset<StagingBuffer> upload_buffers; std::multiset<StagingBuffer> upload_buffers;
std::multiset<StagingBuffer> download_buffers; std::multiset<StagingBuffer> download_buffers;
OGLFramebuffer read_fbo, draw_fbo;
// Recycled textures to reduce driver allocation overhead
std::unordered_multimap<VideoCore::HostTextureTag, OGLTexture> texture_recycler;
}; };
class CachedSurface : public VideoCore::SurfaceBase<CachedSurface> { class Surface : public VideoCore::SurfaceBase<Surface> {
public: public:
CachedSurface(VideoCore::SurfaceParams& params, TextureRuntime& runtime) Surface(VideoCore::SurfaceParams& params, TextureRuntime& runtime);
: VideoCore::SurfaceBase<CachedSurface>{params}, runtime{runtime} {} ~Surface() override = default;
~CachedSurface() override = default;
/// Uploads pixel data in staging to a rectangle region of the surface texture /// Uploads pixel data in staging to a rectangle region of the surface texture
void UploadTexture(Common::Rectangle<u32> rect, const StagingBuffer& staging); void Upload(const VideoCore::BufferTextureCopy& upload, const StagingBuffer& staging);
/// Downloads pixel data to staging from a rectangle region of the surface texture /// Downloads pixel data to staging from a rectangle region of the surface texture
void DownloadTexture(Common::Rectangle<u32> rect, const StagingBuffer& staging); void Download(const VideoCore::BufferTextureCopy& download, const StagingBuffer& staging);
private: private:
/// Replaces the current texture with a scaled version according to res_scale /// Downloads scaled image by downscaling the requested rectangle
void Scale(); void ScaledDownload(const VideoCore::BufferTextureCopy& download);
/// Uploads pixel data to scaled texture
void ScaledUpload(const VideoCore::BufferTextureCopy& upload);
private: private:
TextureRuntime& runtime; TextureRuntime& runtime;
const Driver& driver;
public: public:
OGLTexture texture{}; OGLTexture texture{};
@ -125,7 +151,7 @@ public:
struct Traits { struct Traits {
using Runtime = TextureRuntime; using Runtime = TextureRuntime;
using Surface = CachedSurface; using Surface = Surface;
}; };
using RasterizerCache = VideoCore::RasterizerCache<Traits>; using RasterizerCache = VideoCore::RasterizerCache<Traits>;

View File

@ -28,9 +28,6 @@ const FormatTuple& GetFormatTuple(VideoCore::PixelFormat format) {
return DEPTH_TUPLES_HACK[static_cast<u32>(format)]; return DEPTH_TUPLES_HACK[static_cast<u32>(format)];
} }
/**
* Self tests for the texture downloader
*/
void TextureDownloaderES::Test() { void TextureDownloaderES::Test() {
auto cur_state = OpenGLState::GetCurState(); auto cur_state = OpenGLState::GetCurState();
OpenGLState state; OpenGLState state;
@ -158,12 +155,8 @@ void main(){
cur_state.Apply(); cur_state.Apply();
} }
/**
* OpenGL ES does not support glReadBuffer for depth/stencil formats
* This gets around it by converting to a Red surface before downloading
*/
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) const {
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;
@ -171,7 +164,7 @@ GLuint TextureDownloaderES::ConvertDepthToColor(GLuint level, GLenum& format, GL
state.draw.vertex_array = vao.handle; state.draw.vertex_array = vao.handle;
OGLTexture texture_view; OGLTexture texture_view;
const ConversionShader* converter; const ConversionShader* converter = nullptr;
switch (type) { switch (type) {
case GL_UNSIGNED_SHORT: case GL_UNSIGNED_SHORT:
state.draw.draw_framebuffer = depth16_fbo.handle; state.draw.draw_framebuffer = depth16_fbo.handle;
@ -192,6 +185,7 @@ GLuint TextureDownloaderES::ConvertDepthToColor(GLuint level, GLenum& format, GL
default: default:
UNREACHABLE_MSG("Destination type not recognized"); UNREACHABLE_MSG("Destination type not recognized");
} }
state.draw.shader_program = converter->program.handle; state.draw.shader_program = converter->program.handle;
state.viewport = {0, 0, width, height}; state.viewport = {0, 0, width, height};
state.Apply(); state.Apply();
@ -210,15 +204,8 @@ GLuint TextureDownloaderES::ConvertDepthToColor(GLuint level, GLenum& format, GL
return state.draw.draw_framebuffer; return state.draw.draw_framebuffer;
} }
/**
* OpenGL ES does not support glGetTexImage. Obtain the pixels by attaching the
* texture to a framebuffer.
* Originally from https://github.com/apitrace/apitrace/blob/master/retrace/glstate_images.cpp
* Depth texture download assumes that the texture's format tuple matches what is found
* OpenGL::depth_format_tuples
*/
void TextureDownloaderES::GetTexImage(GLenum target, GLuint level, GLenum format, GLenum type, void TextureDownloaderES::GetTexImage(GLenum target, GLuint level, GLenum format, GLenum type,
GLint height, GLint width, void* pixels) { GLint height, GLint width, void* pixels) const {
OpenGLState state = OpenGLState::GetCurState(); OpenGLState state = OpenGLState::GetCurState();
GLuint texture; GLuint texture;
const GLuint old_read_buffer = state.draw.read_framebuffer; const GLuint old_read_buffer = state.draw.read_framebuffer;

View File

@ -3,28 +3,40 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#pragma once #pragma once
#include "common/common_types.h" #include "common/common_types.h"
#include "video_core/renderer_opengl/gl_resource_manager.h" #include "video_core/renderer_opengl/gl_resource_manager.h"
namespace OpenGL { namespace OpenGL {
class OpenGLState; class OpenGLState;
class TextureDownloaderES { class TextureDownloaderES {
static constexpr u16 MAX_SIZE = 1024;
public: public:
TextureDownloaderES(bool enable_depth_stencil); TextureDownloaderES(bool enable_depth_stencil);
/**
* OpenGL ES does not support glGetTexImage. Obtain the pixels by attaching the
* texture to a framebuffer.
* Originally from https://github.com/apitrace/apitrace/blob/master/retrace/glstate_images.cpp
* Depth texture download assumes that the texture's format tuple matches what is found
* OpenGL::depth_format_tuples
*/
void GetTexImage(GLenum target, GLuint level, GLenum format, const GLenum type, void GetTexImage(GLenum target, GLuint level, GLenum format, const GLenum type,
GLint height, GLint width, void* pixels); GLint height, GLint width, void* pixels) const;
private: private:
void Test(); /**
* OpenGL ES does not support glReadBuffer for depth/stencil formats.
* This gets around it by converting to a Red surface before downloading
*/
GLuint ConvertDepthToColor(GLuint level, GLenum& format, GLenum& type, GLuint ConvertDepthToColor(GLuint level, GLenum& format, GLenum& type,
GLint height, GLint width); GLint height, GLint width) const;
/// Self tests for the texture downloader
void Test();
private: private:
static constexpr u16 MAX_SIZE = 1024;
struct ConversionShader { struct ConversionShader {
OGLProgram program; OGLProgram program;
GLint lod_location{-1}; GLint lod_location{-1};
@ -40,4 +52,5 @@ private:
ConversionShader d24s8_r32ui_conversion_shader; ConversionShader d24s8_r32ui_conversion_shader;
OGLSampler sampler; OGLSampler sampler;
}; };
} // namespace OpenGL } // namespace OpenGL

View File

@ -60,7 +60,7 @@ bool TextureFilterer::IsNull() const {
bool TextureFilterer::Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect, bool TextureFilterer::Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect,
const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect, const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect,
VideoCore::SurfaceType type) { VideoCore::SurfaceType type) const {
// Depth/Stencil texture filtering is not supported for now // Depth/Stencil texture filtering is not supported for now
if (IsNull() || (type != VideoCore::SurfaceType::Color && type != VideoCore::SurfaceType::Texture)) { if (IsNull() || (type != VideoCore::SurfaceType::Color && type != VideoCore::SurfaceType::Texture)) {

View File

@ -28,7 +28,7 @@ public:
// Returns true if the texture was able to be filtered // Returns true if the texture was able to be filtered
bool Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect, bool Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect,
const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect, const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect,
VideoCore::SurfaceType type); VideoCore::SurfaceType type) const;
static std::vector<std::string_view> GetFilterNames(); static std::vector<std::string_view> GetFilterNames();