gl_texture_runtime: Clean up texture upload/download code
* Improve readability and code clarity
This commit is contained in:
@ -22,6 +22,7 @@ struct Rectangle {
|
||||
: left(left), top(top), right(right), bottom(bottom) {}
|
||||
|
||||
constexpr auto operator<=>(const Rectangle&) const = default;
|
||||
|
||||
constexpr void operator*=(const T value) {
|
||||
left *= value;
|
||||
top *= value;
|
||||
@ -29,6 +30,9 @@ struct Rectangle {
|
||||
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 {
|
||||
return std::abs(static_cast<std::make_signed_t<T>>(right - left));
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
#include <optional>
|
||||
#include <boost/range/iterator_range.hpp>
|
||||
@ -46,11 +47,11 @@ template <class T>
|
||||
class RasterizerCache : NonCopyable {
|
||||
public:
|
||||
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
|
||||
using Surface = std::shared_ptr<CachedSurface>;
|
||||
using Watcher = SurfaceWatcher<CachedSurface>;
|
||||
using SurfaceSet = std::set<Surface>;
|
||||
using SurfaceMap =
|
||||
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
|
||||
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,
|
||||
bool NoUnimplementedReinterpretations(const Surface& surface, SurfaceParams& params,
|
||||
SurfaceInterval interval);
|
||||
@ -268,7 +272,6 @@ bool RasterizerCache<T>::BlitSurfaces(const Surface& src_surface, Common::Rectan
|
||||
dst_surface->InvalidateAllWatcher();
|
||||
|
||||
const TextureBlit texture_blit = {
|
||||
.surface_type = src_surface->type,
|
||||
.src_level = 0,
|
||||
.dst_level = 0,
|
||||
.src_layer = 0,
|
||||
@ -277,7 +280,7 @@ bool RasterizerCache<T>::BlitSurfaces(const Surface& src_surface, Common::Rectan
|
||||
.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;
|
||||
@ -305,21 +308,17 @@ void RasterizerCache<T>::CopySurface(const Surface& src_surface, const Surface&
|
||||
|
||||
const ClearValue clear_value =
|
||||
MakeClearValue(dst_surface->type, dst_surface->pixel_format, fill_buffer.data());
|
||||
|
||||
const TextureClear clear_rect = {
|
||||
.surface_type = dst_surface->type,
|
||||
.texture_format = dst_surface->pixel_format,
|
||||
.texture_level = 0,
|
||||
.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;
|
||||
}
|
||||
|
||||
if (src_surface->CanSubRect(subrect_params)) {
|
||||
const TextureBlit texture_blit = {
|
||||
.surface_type = src_surface->type,
|
||||
.src_level = 0,
|
||||
.dst_level = 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)
|
||||
};
|
||||
|
||||
runtime.BlitTextures(src_surface->texture, dst_surface->texture, texture_blit);
|
||||
runtime.BlitTextures(*src_surface, *dst_surface, texture_blit);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -551,7 +550,6 @@ auto RasterizerCache<T>::GetTextureSurface(const Pica::Texture::TextureInfo& inf
|
||||
|
||||
if (/*texture_filterer->IsNull()*/true) {
|
||||
const TextureBlit texture_blit = {
|
||||
.surface_type = surface->type,
|
||||
.src_level = 0,
|
||||
.dst_level = level,
|
||||
.src_layer = 0,
|
||||
@ -560,7 +558,7 @@ auto RasterizerCache<T>::GetTextureSurface(const Pica::Texture::TextureInfo& inf
|
||||
.dst_rect = surface_params.GetScaledRect()
|
||||
};
|
||||
|
||||
runtime.BlitTextures(level_surface->texture, surface->texture, texture_blit);
|
||||
runtime.BlitTextures(*level_surface, *surface, texture_blit);
|
||||
}
|
||||
|
||||
watcher->Validate();
|
||||
@ -574,6 +572,7 @@ auto RasterizerCache<T>::GetTextureSurface(const Pica::Texture::TextureInfo& inf
|
||||
template <class T>
|
||||
auto RasterizerCache<T>::GetTextureCube(const TextureCubeConfig& config) -> const Surface& {
|
||||
auto& cube = texture_cube_cache[config];
|
||||
cube->is_texture_cube = true;
|
||||
|
||||
struct Face {
|
||||
Face(std::shared_ptr<Watcher>& watcher, PAddr address)
|
||||
@ -633,7 +632,6 @@ auto RasterizerCache<T>::GetTextureCube(const TextureCubeConfig& config) -> cons
|
||||
}
|
||||
|
||||
const TextureBlit texture_blit = {
|
||||
.surface_type = SurfaceType::Color,
|
||||
.src_level = 0,
|
||||
.dst_level = 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}
|
||||
};
|
||||
|
||||
runtime.BlitTextures(surface->texture, cube->texture, texture_blit);
|
||||
runtime.BlitTextures(*surface, *cube, texture_blit);
|
||||
face.watcher->Validate();
|
||||
}
|
||||
}
|
||||
@ -758,7 +756,7 @@ auto RasterizerCache<T>::GetFillSurface(const GPU::Regs::MemoryFillConfig& confi
|
||||
params.type = SurfaceType::Fill;
|
||||
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);
|
||||
if (config.fill_32bit) {
|
||||
@ -903,8 +901,8 @@ void RasterizerCache<T>::UploadSurface(const Surface& surface, SurfaceInterval i
|
||||
return;
|
||||
}
|
||||
|
||||
const u32 start_offset = load_start - surface->addr;
|
||||
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());
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
@ -937,38 +942,28 @@ void RasterizerCache<T>::DownloadSurface(const Surface& surface, SurfaceInterval
|
||||
|
||||
const auto& 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);
|
||||
}
|
||||
const SurfaceParams params = surface->FromInterval(interval);
|
||||
const BufferTextureCopy download = {
|
||||
.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);
|
||||
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());
|
||||
const u32 start_offset = flush_start - surface->addr;
|
||||
const u32 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) {
|
||||
if (!surface->is_tiled) {
|
||||
ASSERT(surface->type == SurfaceType::Color);
|
||||
|
||||
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>
|
||||
bool RasterizerCache<T>::NoUnimplementedReinterpretations(const Surface& surface, SurfaceParams& params,
|
||||
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::RGBA4, PixelFormat::IA8, PixelFormat::RG8, PixelFormat::I8,
|
||||
PixelFormat::A8, PixelFormat::IA4, PixelFormat::I4, PixelFormat::A4,
|
||||
PixelFormat::ETC1, PixelFormat::ETC1A4, PixelFormat::D16, PixelFormat::D24,
|
||||
PixelFormat::D24S8,
|
||||
};
|
||||
|
||||
bool implemented = true;
|
||||
for (PixelFormat format : all_formats) {
|
||||
if (GetFormatBpp(format) == surface->GetFormatBpp()) {
|
||||
@ -1017,13 +1044,16 @@ bool RasterizerCache<T>::NoUnimplementedReinterpretations(const Surface& surface
|
||||
template <class T>
|
||||
bool RasterizerCache<T>::IntervalHasInvalidPixelFormat(SurfaceParams& params, SurfaceInterval interval) {
|
||||
params.pixel_format = PixelFormat::Invalid;
|
||||
for (const auto& set : RangeFromInterval(surface_cache, interval))
|
||||
for (const auto& surface : set.second)
|
||||
for (const auto& set : RangeFromInterval(surface_cache, interval)) {
|
||||
for (const auto& surface : set.second) {
|
||||
if (surface->pixel_format == PixelFormat::Invalid) {
|
||||
LOG_DEBUG(Render_OpenGL, "Surface {:#x} found with invalid pixel format",
|
||||
surface->addr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
ASSERT(surface->IsRegionValid(interval));
|
||||
|
||||
DownloadSurface(surface, interval);
|
||||
if (surface->type == SurfaceType::Fill) {
|
||||
DownloadFillSurface(surface, interval);
|
||||
} else {
|
||||
DownloadSurface(surface, interval);
|
||||
}
|
||||
|
||||
flushed_intervals += interval;
|
||||
}
|
||||
|
||||
@ -1132,11 +1167,11 @@ template <class T>
|
||||
void RasterizerCache<T>::InvalidateRegion(PAddr addr, u32 size, const Surface& region_owner) {
|
||||
std::lock_guard lock{mutex};
|
||||
|
||||
if (size == 0)
|
||||
if (size == 0) [[unlikely]] {
|
||||
return;
|
||||
}
|
||||
|
||||
const SurfaceInterval invalid_interval(addr, addr + size);
|
||||
|
||||
const SurfaceInterval invalid_interval{addr, addr + size};
|
||||
if (region_owner != nullptr) {
|
||||
ASSERT(region_owner->type != SurfaceType::Texture);
|
||||
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>
|
||||
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());
|
||||
|
||||
// Allocate surface texture
|
||||
|
@ -3,9 +3,9 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
#include "common/common_types.h"
|
||||
#include "common/math_util.h"
|
||||
#include "common/vector_math.h"
|
||||
#include "video_core/rasterizer_cache/pixel_format.h"
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
@ -34,14 +34,11 @@ union ClearValue {
|
||||
};
|
||||
|
||||
struct TextureClear {
|
||||
SurfaceType surface_type;
|
||||
PixelFormat texture_format;
|
||||
u32 texture_level;
|
||||
Rect2D texture_rect;
|
||||
};
|
||||
|
||||
struct TextureCopy {
|
||||
SurfaceType surface_type;
|
||||
u32 src_level;
|
||||
u32 dst_level;
|
||||
Offset src_offset;
|
||||
@ -50,7 +47,6 @@ struct TextureCopy {
|
||||
};
|
||||
|
||||
struct TextureBlit {
|
||||
SurfaceType surface_type;
|
||||
u32 src_level;
|
||||
u32 dst_level;
|
||||
u32 src_layer;
|
||||
@ -62,9 +58,6 @@ struct TextureBlit {
|
||||
struct BufferTextureCopy {
|
||||
u32 buffer_offset;
|
||||
u32 buffer_size;
|
||||
u32 buffer_row_length;
|
||||
u32 buffer_height;
|
||||
SurfaceType surface_type;
|
||||
Rect2D texture_rect;
|
||||
u32 texture_level;
|
||||
};
|
||||
|
@ -52,7 +52,10 @@ GLbitfield MakeBufferMask(VideoCore::SurfaceType type) {
|
||||
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();
|
||||
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) {
|
||||
const auto& tuple = GetFormatTuple(format);
|
||||
auto recycled_tex = texture2d_recycler.find({format, width, height});
|
||||
if (recycled_tex != texture2d_recycler.end()) {
|
||||
auto recycled_tex = texture_recycler.find({format, width, height});
|
||||
if (recycled_tex != texture_recycler.end()) {
|
||||
OGLTexture texture = std::move(recycled_tex->second);
|
||||
texture2d_recycler.erase(recycled_tex);
|
||||
texture_recycler.erase(recycled_tex);
|
||||
return texture;
|
||||
}
|
||||
|
||||
@ -151,48 +154,7 @@ OGLTexture TextureRuntime::AllocateCubeMap(u32 width, VideoCore::PixelFormat for
|
||||
return texture;
|
||||
}
|
||||
|
||||
void TextureRuntime::ReadTexture(OGLTexture& texture, const VideoCore::BufferTextureCopy& copy,
|
||||
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,
|
||||
bool TextureRuntime::ClearTexture(Surface& surface, const VideoCore::TextureClear& clear,
|
||||
VideoCore::ClearValue value) {
|
||||
OpenGLState prev_state = OpenGLState::GetCurState();
|
||||
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.Apply();
|
||||
|
||||
switch (clear.surface_type) {
|
||||
GLint handle = surface.texture.handle;
|
||||
switch (surface.type) {
|
||||
case VideoCore::SurfaceType::Color:
|
||||
case VideoCore::SurfaceType::Texture:
|
||||
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);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
|
||||
0);
|
||||
@ -226,7 +189,7 @@ bool TextureRuntime::ClearTexture(OGLTexture& texture, const VideoCore::TextureC
|
||||
break;
|
||||
case VideoCore::SurfaceType::Depth:
|
||||
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);
|
||||
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:
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||
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.stencil.write_mask = -1;
|
||||
@ -253,11 +216,11 @@ bool TextureRuntime::ClearTexture(OGLTexture& texture, const VideoCore::TextureC
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
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.Apply();
|
||||
|
||||
auto BindAttachment = [&blit, &source, &dest](GLenum attachment, u32 src_tex, u32 dst_tex) -> void {
|
||||
const GLenum src_target = source.target == GL_TEXTURE_CUBE_MAP ?
|
||||
GL_TEXTURE_CUBE_MAP_POSITIVE_X + blit.src_layer : source.target;
|
||||
const GLenum dst_target = dest.target == GL_TEXTURE_CUBE_MAP ?
|
||||
GL_TEXTURE_CUBE_MAP_POSITIVE_X + blit.dst_layer : dest.target;
|
||||
const GLenum src_textarget = source.is_texture_cube ?
|
||||
GL_TEXTURE_CUBE_MAP_POSITIVE_X + blit.src_layer : GL_TEXTURE_2D;
|
||||
BindFramebuffer(GL_READ_FRAMEBUFFER, blit.src_level, src_textarget, source.type, source.texture);
|
||||
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, attachment, src_target, src_tex, blit.src_level);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, dst_target, dst_tex, blit.dst_level);
|
||||
};
|
||||
|
||||
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!");
|
||||
}
|
||||
const GLenum dst_textarget = dest.is_texture_cube ?
|
||||
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);
|
||||
|
||||
// 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
|
||||
// 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
|
||||
// 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;
|
||||
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,
|
||||
@ -313,166 +251,165 @@ bool TextureRuntime::BlitTextures(OGLTexture& source, OGLTexture& dest, const Vi
|
||||
return true;
|
||||
}
|
||||
|
||||
void TextureRuntime::GenerateMipmaps(OGLTexture& texture, u32 max_level) {
|
||||
void TextureRuntime::GenerateMipmaps(Surface& surface, u32 max_level) {
|
||||
OpenGLState prev_state = OpenGLState::GetCurState();
|
||||
SCOPE_EXIT({ prev_state.Apply(); });
|
||||
|
||||
OpenGLState state{};
|
||||
state.texture_units[0].texture_2d = texture.handle;
|
||||
state.texture_units[0].texture_2d = surface.texture.handle;
|
||||
state.Apply();
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, max_level);
|
||||
|
||||
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));
|
||||
void CachedSurface::UploadTexture(Common::Rectangle<u32> rect, const StagingBuffer& staging) {
|
||||
void Surface::Upload(const VideoCore::BufferTextureCopy& upload, const StagingBuffer& staging) {
|
||||
MICROPROFILE_SCOPE(RasterizerCache_TextureUL);
|
||||
|
||||
const FormatTuple& tuple = runtime.GetFormatTuple(pixel_format);
|
||||
|
||||
// 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;
|
||||
}
|
||||
// Ensure no bad interactions with GL_UNPACK_ALIGNMENT
|
||||
ASSERT(stride * GetBytesPerPixel(pixel_format) % 4 == 0);
|
||||
|
||||
OpenGLState prev_state = OpenGLState::GetCurState();
|
||||
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));
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, staging.buffer.handle);
|
||||
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast<GLsizei>(rect.GetWidth()),
|
||||
static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
|
||||
reinterpret_cast<void*>(buffer_offset));
|
||||
const bool is_scaled = res_scale != 1;
|
||||
if (is_scaled) {
|
||||
ScaledUpload(upload);
|
||||
} else {
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, texture.handle);
|
||||
|
||||
// Lock the staging buffer until glTexSubImage completes
|
||||
staging.Lock();
|
||||
const auto& tuple = runtime.GetFormatTuple(pixel_format);
|
||||
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);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
|
||||
if (res_scale != 1) {
|
||||
auto scaled_rect = rect;
|
||||
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);
|
||||
}*/
|
||||
}
|
||||
|
||||
// Lock the staging buffer until glTexSubImage completes
|
||||
staging.Lock();
|
||||
InvalidateAllWatcher();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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
|
||||
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));
|
||||
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
|
||||
if (res_scale != 1) {
|
||||
auto scaled_rect = rect;
|
||||
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));
|
||||
const bool is_scaled = res_scale != 1;
|
||||
if (is_scaled) {
|
||||
ScaledDownload(download);
|
||||
} else {
|
||||
const u32 download_size = width * height * GetBytesPerPixel(pixel_format);
|
||||
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.BindFramebuffer(GL_READ_FRAMEBUFFER, download.texture_level, GL_TEXTURE_2D, type, texture);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -9,6 +9,8 @@
|
||||
#include "video_core/rasterizer_cache/surface_base.h"
|
||||
#include "video_core/rasterizer_cache/types.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 {
|
||||
|
||||
@ -50,13 +52,14 @@ struct StagingBuffer {
|
||||
};
|
||||
|
||||
class Driver;
|
||||
class Surface;
|
||||
|
||||
/**
|
||||
* Provides texture manipulation functions to the rasterizer cache
|
||||
* Separating this into a class makes it easier to abstract graphics API code
|
||||
*/
|
||||
class TextureRuntime {
|
||||
friend class CachedSurface;
|
||||
friend class Surface;
|
||||
public:
|
||||
TextureRuntime(Driver& driver);
|
||||
~TextureRuntime() = default;
|
||||
@ -73,51 +76,74 @@ public:
|
||||
/// Allocates an OpenGL cube map texture with the specified dimentions and 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
|
||||
bool ClearTexture(OGLTexture& texture, const VideoCore::TextureClear& clear,
|
||||
bool ClearTexture(Surface& surface, const VideoCore::TextureClear& clear,
|
||||
VideoCore::ClearValue value);
|
||||
|
||||
/// 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
|
||||
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
|
||||
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:
|
||||
Driver& driver;
|
||||
OGLFramebuffer read_fbo, draw_fbo;
|
||||
std::unordered_multimap<VideoCore::HostTextureTag, OGLTexture> texture2d_recycler;
|
||||
TextureDownloaderES downloader_es;
|
||||
TextureFilterer filterer;
|
||||
|
||||
// Staging buffers stored in increasing size
|
||||
std::multiset<StagingBuffer> upload_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:
|
||||
CachedSurface(VideoCore::SurfaceParams& params, TextureRuntime& runtime)
|
||||
: VideoCore::SurfaceBase<CachedSurface>{params}, runtime{runtime} {}
|
||||
~CachedSurface() override = default;
|
||||
Surface(VideoCore::SurfaceParams& params, TextureRuntime& runtime);
|
||||
~Surface() override = default;
|
||||
|
||||
/// 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
|
||||
void DownloadTexture(Common::Rectangle<u32> rect, const StagingBuffer& staging);
|
||||
void Download(const VideoCore::BufferTextureCopy& download, const StagingBuffer& staging);
|
||||
|
||||
private:
|
||||
/// Replaces the current texture with a scaled version according to res_scale
|
||||
void Scale();
|
||||
/// Downloads scaled image by downscaling the requested rectangle
|
||||
void ScaledDownload(const VideoCore::BufferTextureCopy& download);
|
||||
|
||||
/// Uploads pixel data to scaled texture
|
||||
void ScaledUpload(const VideoCore::BufferTextureCopy& upload);
|
||||
|
||||
private:
|
||||
TextureRuntime& runtime;
|
||||
const Driver& driver;
|
||||
|
||||
public:
|
||||
OGLTexture texture{};
|
||||
@ -125,7 +151,7 @@ public:
|
||||
|
||||
struct Traits {
|
||||
using Runtime = TextureRuntime;
|
||||
using Surface = CachedSurface;
|
||||
using Surface = Surface;
|
||||
};
|
||||
|
||||
using RasterizerCache = VideoCore::RasterizerCache<Traits>;
|
||||
|
@ -28,9 +28,6 @@ const FormatTuple& GetFormatTuple(VideoCore::PixelFormat format) {
|
||||
return DEPTH_TUPLES_HACK[static_cast<u32>(format)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Self tests for the texture downloader
|
||||
*/
|
||||
void TextureDownloaderES::Test() {
|
||||
auto cur_state = OpenGLState::GetCurState();
|
||||
OpenGLState state;
|
||||
@ -158,12 +155,8 @@ void main(){
|
||||
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,
|
||||
GLint height, GLint width) {
|
||||
GLint height, GLint width) const {
|
||||
ASSERT(width <= MAX_SIZE && height <= MAX_SIZE);
|
||||
const OpenGLState cur_state = OpenGLState::GetCurState();
|
||||
OpenGLState state;
|
||||
@ -171,7 +164,7 @@ GLuint TextureDownloaderES::ConvertDepthToColor(GLuint level, GLenum& format, GL
|
||||
state.draw.vertex_array = vao.handle;
|
||||
|
||||
OGLTexture texture_view;
|
||||
const ConversionShader* converter;
|
||||
const ConversionShader* converter = nullptr;
|
||||
switch (type) {
|
||||
case GL_UNSIGNED_SHORT:
|
||||
state.draw.draw_framebuffer = depth16_fbo.handle;
|
||||
@ -192,6 +185,7 @@ GLuint TextureDownloaderES::ConvertDepthToColor(GLuint level, GLenum& format, GL
|
||||
default:
|
||||
UNREACHABLE_MSG("Destination type not recognized");
|
||||
}
|
||||
|
||||
state.draw.shader_program = converter->program.handle;
|
||||
state.viewport = {0, 0, width, height};
|
||||
state.Apply();
|
||||
@ -210,15 +204,8 @@ GLuint TextureDownloaderES::ConvertDepthToColor(GLuint level, GLenum& format, GL
|
||||
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,
|
||||
GLint height, GLint width, void* pixels) {
|
||||
GLint height, GLint width, void* pixels) const {
|
||||
OpenGLState state = OpenGLState::GetCurState();
|
||||
GLuint texture;
|
||||
const GLuint old_read_buffer = state.draw.read_framebuffer;
|
||||
|
@ -3,28 +3,40 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
class OpenGLState;
|
||||
|
||||
class TextureDownloaderES {
|
||||
static constexpr u16 MAX_SIZE = 1024;
|
||||
public:
|
||||
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,
|
||||
GLint height, GLint width, void* pixels);
|
||||
GLint height, GLint width, void* pixels) const;
|
||||
|
||||
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,
|
||||
GLint height, GLint width);
|
||||
GLint height, GLint width) const;
|
||||
|
||||
/// Self tests for the texture downloader
|
||||
void Test();
|
||||
|
||||
private:
|
||||
static constexpr u16 MAX_SIZE = 1024;
|
||||
|
||||
struct ConversionShader {
|
||||
OGLProgram program;
|
||||
GLint lod_location{-1};
|
||||
@ -40,4 +52,5 @@ private:
|
||||
ConversionShader d24s8_r32ui_conversion_shader;
|
||||
OGLSampler sampler;
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
|
@ -60,7 +60,7 @@ bool TextureFilterer::IsNull() const {
|
||||
|
||||
bool TextureFilterer::Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_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
|
||||
if (IsNull() || (type != VideoCore::SurfaceType::Color && type != VideoCore::SurfaceType::Texture)) {
|
||||
|
@ -28,7 +28,7 @@ public:
|
||||
// Returns true if the texture was able to be filtered
|
||||
bool Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect,
|
||||
const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect,
|
||||
VideoCore::SurfaceType type);
|
||||
VideoCore::SurfaceType type) const;
|
||||
|
||||
static std::vector<std::string_view> GetFilterNames();
|
||||
|
||||
|
Reference in New Issue
Block a user