2019-04-11 22:14:55 +02:00
|
|
|
// Copyright 2019 yuzu Emulator Project
|
|
|
|
// Licensed under GPLv2 or any later version
|
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
|
|
|
#include "common/assert.h"
|
|
|
|
#include "common/common_types.h"
|
2019-05-07 16:57:16 +02:00
|
|
|
#include "common/microprofile.h"
|
2019-04-11 22:14:55 +02:00
|
|
|
#include "common/scope_exit.h"
|
2019-05-07 16:57:16 +02:00
|
|
|
#include "core/core.h"
|
2019-04-11 22:14:55 +02:00
|
|
|
#include "video_core/morton.h"
|
|
|
|
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
2019-05-08 05:13:05 +02:00
|
|
|
#include "video_core/renderer_opengl/gl_state.h"
|
2019-04-11 22:14:55 +02:00
|
|
|
#include "video_core/renderer_opengl/gl_texture_cache.h"
|
2019-04-17 01:01:07 +02:00
|
|
|
#include "video_core/renderer_opengl/utils.h"
|
2019-04-25 18:41:57 +02:00
|
|
|
#include "video_core/texture_cache/texture_cache.h"
|
2019-04-11 22:14:55 +02:00
|
|
|
#include "video_core/textures/convert.h"
|
|
|
|
#include "video_core/textures/texture.h"
|
|
|
|
|
|
|
|
namespace OpenGL {
|
|
|
|
|
|
|
|
using Tegra::Texture::SwizzleSource;
|
|
|
|
using VideoCore::MortonSwizzleMode;
|
|
|
|
|
2019-04-25 18:41:57 +02:00
|
|
|
using VideoCore::Surface::ComponentType;
|
|
|
|
using VideoCore::Surface::PixelFormat;
|
2019-05-13 02:33:52 +02:00
|
|
|
using VideoCore::Surface::SurfaceCompression;
|
2019-04-25 18:41:57 +02:00
|
|
|
using VideoCore::Surface::SurfaceTarget;
|
2019-05-08 05:13:05 +02:00
|
|
|
using VideoCore::Surface::SurfaceType;
|
2019-04-25 18:41:57 +02:00
|
|
|
|
2019-05-07 16:57:16 +02:00
|
|
|
MICROPROFILE_DEFINE(OpenGL_Texture_Upload, "OpenGL", "Texture Upload", MP_RGB(128, 192, 128));
|
|
|
|
MICROPROFILE_DEFINE(OpenGL_Texture_Download, "OpenGL", "Texture Download", MP_RGB(128, 192, 128));
|
|
|
|
|
2019-04-11 22:14:55 +02:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
struct FormatTuple {
|
|
|
|
GLint internal_format;
|
|
|
|
GLenum format;
|
|
|
|
GLenum type;
|
|
|
|
ComponentType component_type;
|
|
|
|
bool compressed;
|
|
|
|
};
|
|
|
|
|
|
|
|
constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex_format_tuples = {{
|
|
|
|
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, false}, // ABGR8U
|
|
|
|
{GL_RGBA8, GL_RGBA, GL_BYTE, ComponentType::SNorm, false}, // ABGR8S
|
|
|
|
{GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, ComponentType::UInt, false}, // ABGR8UI
|
|
|
|
{GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV, ComponentType::UNorm, false}, // B5G6R5U
|
|
|
|
{GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, ComponentType::UNorm,
|
|
|
|
false}, // A2B10G10R10U
|
|
|
|
{GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, ComponentType::UNorm, false}, // A1B5G5R5U
|
|
|
|
{GL_R8, GL_RED, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // R8U
|
|
|
|
{GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, ComponentType::UInt, false}, // R8UI
|
|
|
|
{GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, ComponentType::Float, false}, // RGBA16F
|
|
|
|
{GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT, ComponentType::UNorm, false}, // RGBA16U
|
|
|
|
{GL_RGBA16UI, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT, ComponentType::UInt, false}, // RGBA16UI
|
|
|
|
{GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, ComponentType::Float,
|
|
|
|
false}, // R11FG11FB10F
|
|
|
|
{GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RGBA32UI
|
|
|
|
{GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
|
|
|
|
true}, // DXT1
|
|
|
|
{GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
|
|
|
|
true}, // DXT23
|
|
|
|
{GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
|
|
|
|
true}, // DXT45
|
|
|
|
{GL_COMPRESSED_RED_RGTC1, GL_RED, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, true}, // DXN1
|
|
|
|
{GL_COMPRESSED_RG_RGTC2, GL_RG, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
|
|
|
|
true}, // DXN2UNORM
|
|
|
|
{GL_COMPRESSED_SIGNED_RG_RGTC2, GL_RG, GL_INT, ComponentType::SNorm, true}, // DXN2SNORM
|
|
|
|
{GL_COMPRESSED_RGBA_BPTC_UNORM, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
|
|
|
|
true}, // BC7U
|
|
|
|
{GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::Float,
|
|
|
|
true}, // BC6H_UF16
|
|
|
|
{GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::Float,
|
|
|
|
true}, // BC6H_SF16
|
|
|
|
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4
|
|
|
|
{GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // BGRA8
|
|
|
|
{GL_RGBA32F, GL_RGBA, GL_FLOAT, ComponentType::Float, false}, // RGBA32F
|
|
|
|
{GL_RG32F, GL_RG, GL_FLOAT, ComponentType::Float, false}, // RG32F
|
|
|
|
{GL_R32F, GL_RED, GL_FLOAT, ComponentType::Float, false}, // R32F
|
|
|
|
{GL_R16F, GL_RED, GL_HALF_FLOAT, ComponentType::Float, false}, // R16F
|
|
|
|
{GL_R16, GL_RED, GL_UNSIGNED_SHORT, ComponentType::UNorm, false}, // R16U
|
|
|
|
{GL_R16_SNORM, GL_RED, GL_SHORT, ComponentType::SNorm, false}, // R16S
|
|
|
|
{GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT, ComponentType::UInt, false}, // R16UI
|
|
|
|
{GL_R16I, GL_RED_INTEGER, GL_SHORT, ComponentType::SInt, false}, // R16I
|
|
|
|
{GL_RG16, GL_RG, GL_UNSIGNED_SHORT, ComponentType::UNorm, false}, // RG16
|
|
|
|
{GL_RG16F, GL_RG, GL_HALF_FLOAT, ComponentType::Float, false}, // RG16F
|
|
|
|
{GL_RG16UI, GL_RG_INTEGER, GL_UNSIGNED_SHORT, ComponentType::UInt, false}, // RG16UI
|
|
|
|
{GL_RG16I, GL_RG_INTEGER, GL_SHORT, ComponentType::SInt, false}, // RG16I
|
|
|
|
{GL_RG16_SNORM, GL_RG, GL_SHORT, ComponentType::SNorm, false}, // RG16S
|
|
|
|
{GL_RGB32F, GL_RGB, GL_FLOAT, ComponentType::Float, false}, // RGB32F
|
|
|
|
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm,
|
|
|
|
false}, // RGBA8_SRGB
|
|
|
|
{GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // RG8U
|
|
|
|
{GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false}, // RG8S
|
|
|
|
{GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI
|
|
|
|
{GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI
|
|
|
|
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8
|
|
|
|
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5
|
|
|
|
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4
|
|
|
|
{GL_SRGB8_ALPHA8, GL_BGRA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // BGRA8
|
|
|
|
// Compressed sRGB formats
|
|
|
|
{GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
|
|
|
|
true}, // DXT1_SRGB
|
|
|
|
{GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
|
|
|
|
true}, // DXT23_SRGB
|
|
|
|
{GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
|
|
|
|
true}, // DXT45_SRGB
|
|
|
|
{GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
|
|
|
|
true}, // BC7U_SRGB
|
|
|
|
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4_SRGB
|
|
|
|
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8_SRGB
|
|
|
|
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5_SRGB
|
|
|
|
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4_SRGB
|
|
|
|
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X5
|
|
|
|
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X5_SRGB
|
|
|
|
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_10X8
|
|
|
|
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_10X8_SRGB
|
|
|
|
|
|
|
|
// Depth formats
|
|
|
|
{GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F
|
|
|
|
{GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, ComponentType::UNorm,
|
|
|
|
false}, // Z16
|
|
|
|
|
|
|
|
// DepthStencil formats
|
|
|
|
{GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, ComponentType::UNorm,
|
|
|
|
false}, // Z24S8
|
|
|
|
{GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, ComponentType::UNorm,
|
|
|
|
false}, // S8Z24
|
|
|
|
{GL_DEPTH32F_STENCIL8, GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV,
|
|
|
|
ComponentType::Float, false}, // Z32FS8
|
|
|
|
}};
|
|
|
|
|
|
|
|
const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType component_type) {
|
|
|
|
ASSERT(static_cast<std::size_t>(pixel_format) < tex_format_tuples.size());
|
|
|
|
const auto& format{tex_format_tuples[static_cast<std::size_t>(pixel_format)]};
|
|
|
|
ASSERT(component_type == format.component_type);
|
|
|
|
return format;
|
|
|
|
}
|
|
|
|
|
2019-05-07 16:57:16 +02:00
|
|
|
GLenum GetTextureTarget(const SurfaceTarget& target) {
|
|
|
|
switch (target) {
|
2019-04-11 22:14:55 +02:00
|
|
|
case SurfaceTarget::Texture1D:
|
|
|
|
return GL_TEXTURE_1D;
|
|
|
|
case SurfaceTarget::Texture2D:
|
|
|
|
return GL_TEXTURE_2D;
|
|
|
|
case SurfaceTarget::Texture3D:
|
|
|
|
return GL_TEXTURE_3D;
|
|
|
|
case SurfaceTarget::Texture1DArray:
|
|
|
|
return GL_TEXTURE_1D_ARRAY;
|
|
|
|
case SurfaceTarget::Texture2DArray:
|
|
|
|
return GL_TEXTURE_2D_ARRAY;
|
|
|
|
case SurfaceTarget::TextureCubemap:
|
|
|
|
return GL_TEXTURE_CUBE_MAP;
|
|
|
|
case SurfaceTarget::TextureCubeArray:
|
|
|
|
return GL_TEXTURE_CUBE_MAP_ARRAY;
|
|
|
|
}
|
|
|
|
UNREACHABLE();
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
GLint GetSwizzleSource(SwizzleSource source) {
|
|
|
|
switch (source) {
|
|
|
|
case SwizzleSource::Zero:
|
|
|
|
return GL_ZERO;
|
|
|
|
case SwizzleSource::R:
|
|
|
|
return GL_RED;
|
|
|
|
case SwizzleSource::G:
|
|
|
|
return GL_GREEN;
|
|
|
|
case SwizzleSource::B:
|
|
|
|
return GL_BLUE;
|
|
|
|
case SwizzleSource::A:
|
|
|
|
return GL_ALPHA;
|
|
|
|
case SwizzleSource::OneInt:
|
|
|
|
case SwizzleSource::OneFloat:
|
|
|
|
return GL_ONE;
|
|
|
|
}
|
|
|
|
UNREACHABLE();
|
|
|
|
return GL_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ApplyTextureDefaults(const SurfaceParams& params, GLuint texture) {
|
|
|
|
glTextureParameteri(texture, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
|
|
glTextureParameteri(texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
glTextureParameteri(texture, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
|
|
glTextureParameteri(texture, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
2019-05-07 16:57:16 +02:00
|
|
|
glTextureParameteri(texture, GL_TEXTURE_MAX_LEVEL, params.num_levels - 1);
|
|
|
|
if (params.num_levels == 1) {
|
2019-04-11 22:14:55 +02:00
|
|
|
glTextureParameterf(texture, GL_TEXTURE_LOD_BIAS, 1000.0f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-14 06:44:16 +02:00
|
|
|
OGLTexture CreateTexture(const SurfaceParams& params, GLenum target, GLenum internal_format) {
|
2019-04-11 22:14:55 +02:00
|
|
|
OGLTexture texture;
|
2019-04-14 06:44:16 +02:00
|
|
|
texture.Create(target);
|
2019-04-11 22:14:55 +02:00
|
|
|
|
2019-05-07 16:57:16 +02:00
|
|
|
switch (params.target) {
|
2019-04-11 22:14:55 +02:00
|
|
|
case SurfaceTarget::Texture1D:
|
2019-05-07 16:57:16 +02:00
|
|
|
glTextureStorage1D(texture.handle, params.num_levels, internal_format, params.width);
|
2019-04-11 22:14:55 +02:00
|
|
|
break;
|
|
|
|
case SurfaceTarget::Texture2D:
|
|
|
|
case SurfaceTarget::TextureCubemap:
|
2019-05-07 16:57:16 +02:00
|
|
|
glTextureStorage2D(texture.handle, params.num_levels, internal_format, params.width,
|
|
|
|
params.height);
|
2019-04-11 22:14:55 +02:00
|
|
|
break;
|
|
|
|
case SurfaceTarget::Texture3D:
|
|
|
|
case SurfaceTarget::Texture2DArray:
|
|
|
|
case SurfaceTarget::TextureCubeArray:
|
2019-05-07 16:57:16 +02:00
|
|
|
glTextureStorage3D(texture.handle, params.num_levels, internal_format, params.width,
|
|
|
|
params.height, params.depth);
|
2019-04-11 22:14:55 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
}
|
|
|
|
|
|
|
|
ApplyTextureDefaults(params, texture.handle);
|
|
|
|
|
|
|
|
return texture;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // Anonymous namespace
|
|
|
|
|
2019-05-07 16:57:16 +02:00
|
|
|
CachedSurface::CachedSurface(const GPUVAddr gpu_addr, const SurfaceParams& params)
|
|
|
|
: VideoCommon::SurfaceBase<View>(gpu_addr, params) {
|
|
|
|
const auto& tuple{GetFormatTuple(params.pixel_format, params.component_type)};
|
2019-04-11 22:14:55 +02:00
|
|
|
internal_format = tuple.internal_format;
|
|
|
|
format = tuple.format;
|
|
|
|
type = tuple.type;
|
|
|
|
is_compressed = tuple.compressed;
|
2019-05-07 16:57:16 +02:00
|
|
|
target = GetTextureTarget(params.target);
|
2019-04-14 06:44:16 +02:00
|
|
|
texture = CreateTexture(params, target, internal_format);
|
2019-05-07 16:57:16 +02:00
|
|
|
DecorateSurfaceName();
|
2019-05-11 06:17:47 +02:00
|
|
|
main_view = CreateViewInner(
|
|
|
|
ViewParams(params.target, 0, params.is_layered ? params.depth : 1, 0, params.num_levels),
|
|
|
|
true);
|
2019-05-07 16:57:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
CachedSurface::~CachedSurface() {
|
|
|
|
views.clear();
|
|
|
|
main_view = nullptr;
|
2019-04-11 22:14:55 +02:00
|
|
|
}
|
|
|
|
|
2019-05-07 16:57:16 +02:00
|
|
|
void CachedSurface::DownloadTexture(std::vector<u8>& staging_buffer) {
|
|
|
|
MICROPROFILE_SCOPE(OpenGL_Texture_Download);
|
2019-04-11 22:14:55 +02:00
|
|
|
|
|
|
|
// TODO(Rodrigo): Optimize alignment
|
|
|
|
SCOPE_EXIT({ glPixelStorei(GL_PACK_ROW_LENGTH, 0); });
|
|
|
|
|
2019-05-07 16:57:16 +02:00
|
|
|
for (u32 level = 0; level < params.num_levels; ++level) {
|
2019-05-13 02:33:52 +02:00
|
|
|
glPixelStorei(GL_PACK_ALIGNMENT, std::min(8U, params.GetRowAlignment(level)));
|
2019-04-11 22:14:55 +02:00
|
|
|
glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.GetMipWidth(level)));
|
2019-05-07 16:57:16 +02:00
|
|
|
const std::size_t mip_offset = params.GetHostMipmapLevelOffset(level);
|
2019-04-11 22:14:55 +02:00
|
|
|
if (is_compressed) {
|
2019-04-24 07:45:03 +02:00
|
|
|
glGetCompressedTextureImage(texture.handle, level,
|
|
|
|
static_cast<GLsizei>(params.GetHostMipmapSize(level)),
|
2019-05-07 16:57:16 +02:00
|
|
|
staging_buffer.data() + mip_offset);
|
2019-04-11 22:14:55 +02:00
|
|
|
} else {
|
|
|
|
glGetTextureImage(texture.handle, level, format, type,
|
|
|
|
static_cast<GLsizei>(params.GetHostMipmapSize(level)),
|
2019-05-07 16:57:16 +02:00
|
|
|
staging_buffer.data() + mip_offset);
|
2019-04-11 22:14:55 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-07 16:57:16 +02:00
|
|
|
void CachedSurface::UploadTexture(std::vector<u8>& staging_buffer) {
|
|
|
|
MICROPROFILE_SCOPE(OpenGL_Texture_Upload);
|
2019-04-24 07:45:03 +02:00
|
|
|
SCOPE_EXIT({ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); });
|
2019-05-07 16:57:16 +02:00
|
|
|
for (u32 level = 0; level < params.num_levels; ++level) {
|
|
|
|
UploadTextureMipmap(level, staging_buffer);
|
2019-04-11 22:14:55 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-07 16:57:16 +02:00
|
|
|
void CachedSurface::UploadTextureMipmap(u32 level, std::vector<u8>& staging_buffer) {
|
2019-04-11 22:14:55 +02:00
|
|
|
// TODO(Rodrigo): Optimize alignment
|
2019-05-13 02:33:52 +02:00
|
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, std::min(8U, params.GetRowAlignment(level)));
|
2019-04-11 22:14:55 +02:00
|
|
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.GetMipWidth(level)));
|
|
|
|
|
2019-05-13 02:33:52 +02:00
|
|
|
auto compression_type = params.GetCompressionType();
|
|
|
|
|
|
|
|
const std::size_t mip_offset = compression_type == SurfaceCompression::Converted
|
|
|
|
? params.GetConvertedMipmapOffset(level)
|
|
|
|
: params.GetHostMipmapLevelOffset(level);
|
2019-05-07 16:57:16 +02:00
|
|
|
u8* buffer{staging_buffer.data() + mip_offset};
|
2019-04-11 22:14:55 +02:00
|
|
|
if (is_compressed) {
|
|
|
|
const auto image_size{static_cast<GLsizei>(params.GetHostMipmapSize(level))};
|
2019-05-07 16:57:16 +02:00
|
|
|
switch (params.target) {
|
2019-04-11 22:14:55 +02:00
|
|
|
case SurfaceTarget::Texture2D:
|
|
|
|
glCompressedTextureSubImage2D(texture.handle, level, 0, 0,
|
|
|
|
static_cast<GLsizei>(params.GetMipWidth(level)),
|
|
|
|
static_cast<GLsizei>(params.GetMipHeight(level)),
|
|
|
|
internal_format, image_size, buffer);
|
|
|
|
break;
|
|
|
|
case SurfaceTarget::Texture3D:
|
|
|
|
case SurfaceTarget::Texture2DArray:
|
|
|
|
case SurfaceTarget::TextureCubeArray:
|
|
|
|
glCompressedTextureSubImage3D(texture.handle, level, 0, 0, 0,
|
|
|
|
static_cast<GLsizei>(params.GetMipWidth(level)),
|
|
|
|
static_cast<GLsizei>(params.GetMipHeight(level)),
|
|
|
|
static_cast<GLsizei>(params.GetMipDepth(level)),
|
|
|
|
internal_format, image_size, buffer);
|
|
|
|
break;
|
|
|
|
case SurfaceTarget::TextureCubemap: {
|
|
|
|
const std::size_t layer_size{params.GetHostLayerSize(level)};
|
2019-05-07 16:57:16 +02:00
|
|
|
for (std::size_t face = 0; face < params.depth; ++face) {
|
2019-04-11 22:14:55 +02:00
|
|
|
glCompressedTextureSubImage3D(texture.handle, level, 0, 0, static_cast<GLint>(face),
|
|
|
|
static_cast<GLsizei>(params.GetMipWidth(level)),
|
|
|
|
static_cast<GLsizei>(params.GetMipHeight(level)), 1,
|
|
|
|
internal_format, static_cast<GLsizei>(layer_size),
|
|
|
|
buffer);
|
|
|
|
buffer += layer_size;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
}
|
|
|
|
} else {
|
2019-05-07 16:57:16 +02:00
|
|
|
switch (params.target) {
|
2019-04-11 22:14:55 +02:00
|
|
|
case SurfaceTarget::Texture1D:
|
|
|
|
glTextureSubImage1D(texture.handle, level, 0, params.GetMipWidth(level), format, type,
|
|
|
|
buffer);
|
|
|
|
break;
|
|
|
|
case SurfaceTarget::Texture1DArray:
|
|
|
|
case SurfaceTarget::Texture2D:
|
|
|
|
glTextureSubImage2D(texture.handle, level, 0, 0, params.GetMipWidth(level),
|
|
|
|
params.GetMipHeight(level), format, type, buffer);
|
|
|
|
break;
|
|
|
|
case SurfaceTarget::Texture3D:
|
|
|
|
case SurfaceTarget::Texture2DArray:
|
|
|
|
case SurfaceTarget::TextureCubeArray:
|
|
|
|
glTextureSubImage3D(
|
|
|
|
texture.handle, level, 0, 0, 0, static_cast<GLsizei>(params.GetMipWidth(level)),
|
|
|
|
static_cast<GLsizei>(params.GetMipHeight(level)),
|
|
|
|
static_cast<GLsizei>(params.GetMipDepth(level)), format, type, buffer);
|
|
|
|
break;
|
|
|
|
case SurfaceTarget::TextureCubemap:
|
2019-05-07 16:57:16 +02:00
|
|
|
for (std::size_t face = 0; face < params.depth; ++face) {
|
2019-04-11 22:14:55 +02:00
|
|
|
glTextureSubImage3D(texture.handle, level, 0, 0, static_cast<GLint>(face),
|
|
|
|
params.GetMipWidth(level), params.GetMipHeight(level), 1,
|
|
|
|
format, type, buffer);
|
|
|
|
buffer += params.GetHostLayerSize(level);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-17 01:01:07 +02:00
|
|
|
void CachedSurface::DecorateSurfaceName() {
|
2019-05-07 16:57:16 +02:00
|
|
|
LabelGLObject(GL_TEXTURE, texture.handle, GetGpuAddr(), params.TargetName());
|
|
|
|
}
|
|
|
|
|
|
|
|
void CachedSurfaceView::DecorateViewName(GPUVAddr gpu_addr, std::string prefix) {
|
2019-05-08 01:09:34 +02:00
|
|
|
LabelGLObject(GL_TEXTURE, texture_view.handle, gpu_addr, prefix);
|
2019-04-17 01:01:07 +02:00
|
|
|
}
|
|
|
|
|
2019-05-07 16:57:16 +02:00
|
|
|
View CachedSurface::CreateView(const ViewParams& view_key) {
|
2019-05-11 06:17:47 +02:00
|
|
|
return CreateViewInner(view_key, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
View CachedSurface::CreateViewInner(const ViewParams& view_key, const bool is_proxy) {
|
|
|
|
auto view = std::make_shared<CachedSurfaceView>(*this, view_key, is_proxy);
|
2019-05-07 16:57:16 +02:00
|
|
|
views[view_key] = view;
|
2019-05-11 06:17:47 +02:00
|
|
|
if (!is_proxy)
|
|
|
|
view->DecorateViewName(gpu_addr, params.TargetName() + "V:" + std::to_string(view_count++));
|
2019-05-07 16:57:16 +02:00
|
|
|
return view;
|
2019-04-11 22:14:55 +02:00
|
|
|
}
|
|
|
|
|
2019-05-11 06:17:47 +02:00
|
|
|
CachedSurfaceView::CachedSurfaceView(CachedSurface& surface, const ViewParams& params,
|
|
|
|
const bool is_proxy)
|
|
|
|
: VideoCommon::ViewBase(params), surface{surface}, is_proxy{is_proxy} {
|
2019-05-07 16:57:16 +02:00
|
|
|
target = GetTextureTarget(params.target);
|
2019-05-11 06:17:47 +02:00
|
|
|
if (!is_proxy) {
|
|
|
|
texture_view = CreateTextureView();
|
|
|
|
}
|
2019-05-08 01:09:34 +02:00
|
|
|
swizzle = EncodeSwizzle(SwizzleSource::R, SwizzleSource::G, SwizzleSource::B, SwizzleSource::A);
|
2019-05-07 16:57:16 +02:00
|
|
|
}
|
2019-04-11 22:14:55 +02:00
|
|
|
|
|
|
|
CachedSurfaceView::~CachedSurfaceView() = default;
|
|
|
|
|
2019-04-14 23:16:27 +02:00
|
|
|
void CachedSurfaceView::Attach(GLenum attachment) const {
|
2019-05-07 16:57:16 +02:00
|
|
|
ASSERT(params.num_layers == 1 && params.num_levels == 1);
|
2019-04-14 23:16:27 +02:00
|
|
|
|
2019-05-08 01:09:34 +02:00
|
|
|
const auto& owner_params = surface.GetSurfaceParams();
|
|
|
|
|
|
|
|
switch (owner_params.target) {
|
2019-04-14 23:16:27 +02:00
|
|
|
case SurfaceTarget::Texture1D:
|
2019-05-08 01:09:34 +02:00
|
|
|
glFramebufferTexture1D(GL_DRAW_FRAMEBUFFER, attachment, surface.GetTarget(),
|
2019-05-08 03:03:33 +02:00
|
|
|
surface.GetTexture(), params.base_level);
|
2019-04-14 23:16:27 +02:00
|
|
|
break;
|
|
|
|
case SurfaceTarget::Texture2D:
|
2019-05-08 01:09:34 +02:00
|
|
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, surface.GetTarget(),
|
2019-05-08 03:03:33 +02:00
|
|
|
surface.GetTexture(), params.base_level);
|
2019-04-14 23:16:27 +02:00
|
|
|
break;
|
|
|
|
case SurfaceTarget::Texture1DArray:
|
|
|
|
case SurfaceTarget::Texture2DArray:
|
|
|
|
case SurfaceTarget::TextureCubemap:
|
|
|
|
case SurfaceTarget::TextureCubeArray:
|
2019-05-08 01:09:34 +02:00
|
|
|
glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, attachment, surface.GetTexture(),
|
2019-05-07 16:57:16 +02:00
|
|
|
params.base_level, params.base_layer);
|
2019-04-14 23:16:27 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
UNIMPLEMENTED();
|
2019-04-11 22:14:55 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-07 16:57:16 +02:00
|
|
|
void CachedSurfaceView::ApplySwizzle(SwizzleSource x_source, SwizzleSource y_source,
|
2019-04-11 22:14:55 +02:00
|
|
|
SwizzleSource z_source, SwizzleSource w_source) {
|
2019-05-08 01:09:34 +02:00
|
|
|
u32 new_swizzle = EncodeSwizzle(x_source, y_source, z_source, w_source);
|
|
|
|
if (new_swizzle == swizzle)
|
2019-04-11 22:14:55 +02:00
|
|
|
return;
|
2019-05-08 01:09:34 +02:00
|
|
|
swizzle = new_swizzle;
|
2019-04-11 22:14:55 +02:00
|
|
|
const std::array<GLint, 4> gl_swizzle = {GetSwizzleSource(x_source), GetSwizzleSource(y_source),
|
|
|
|
GetSwizzleSource(z_source),
|
|
|
|
GetSwizzleSource(w_source)};
|
2019-05-11 06:17:47 +02:00
|
|
|
const GLuint handle = GetTexture();
|
|
|
|
glTextureParameteriv(handle, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle.data());
|
2019-04-11 22:14:55 +02:00
|
|
|
}
|
|
|
|
|
2019-05-08 01:09:34 +02:00
|
|
|
OGLTextureView CachedSurfaceView::CreateTextureView() const {
|
2019-05-07 16:57:16 +02:00
|
|
|
const auto& owner_params = surface.GetSurfaceParams();
|
2019-05-08 01:09:34 +02:00
|
|
|
OGLTextureView tv;
|
|
|
|
tv.Create();
|
2019-04-11 22:14:55 +02:00
|
|
|
|
2019-05-08 01:09:34 +02:00
|
|
|
const GLuint handle{tv.handle};
|
2019-05-07 16:57:16 +02:00
|
|
|
const FormatTuple& tuple{
|
|
|
|
GetFormatTuple(owner_params.pixel_format, owner_params.component_type)};
|
2019-04-11 22:14:55 +02:00
|
|
|
|
2019-05-07 16:57:16 +02:00
|
|
|
glTextureView(handle, target, surface.texture.handle, tuple.internal_format, params.base_level,
|
|
|
|
params.num_levels, params.base_layer, params.num_layers);
|
2019-04-11 22:14:55 +02:00
|
|
|
|
2019-05-07 16:57:16 +02:00
|
|
|
ApplyTextureDefaults(owner_params, handle);
|
2019-04-11 22:14:55 +02:00
|
|
|
|
2019-05-08 01:09:34 +02:00
|
|
|
return tv;
|
2019-04-11 22:14:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
TextureCacheOpenGL::TextureCacheOpenGL(Core::System& system,
|
|
|
|
VideoCore::RasterizerInterface& rasterizer)
|
2019-05-08 05:13:05 +02:00
|
|
|
: TextureCacheBase{system, rasterizer} {
|
|
|
|
src_framebuffer.Create();
|
|
|
|
dst_framebuffer.Create();
|
|
|
|
}
|
2019-04-11 22:14:55 +02:00
|
|
|
|
|
|
|
TextureCacheOpenGL::~TextureCacheOpenGL() = default;
|
|
|
|
|
2019-05-07 16:57:16 +02:00
|
|
|
Surface TextureCacheOpenGL::CreateSurface(GPUVAddr gpu_addr, const SurfaceParams& params) {
|
|
|
|
return std::make_shared<CachedSurface>(gpu_addr, params);
|
2019-04-15 21:17:27 +02:00
|
|
|
}
|
|
|
|
|
2019-05-07 16:57:16 +02:00
|
|
|
void TextureCacheOpenGL::ImageCopy(Surface src_surface, Surface dst_surface,
|
|
|
|
const VideoCommon::CopyParams& copy_params) {
|
|
|
|
const auto src_handle = src_surface->GetTexture();
|
|
|
|
const auto src_target = src_surface->GetTarget();
|
|
|
|
const auto dst_handle = dst_surface->GetTexture();
|
|
|
|
const auto dst_target = dst_surface->GetTarget();
|
|
|
|
glCopyImageSubData(src_handle, src_target, copy_params.source_level, copy_params.source_x,
|
|
|
|
copy_params.source_y, copy_params.source_z, dst_handle, dst_target,
|
|
|
|
copy_params.dest_level, copy_params.dest_x, copy_params.dest_y,
|
|
|
|
copy_params.dest_z, copy_params.width, copy_params.height,
|
|
|
|
copy_params.depth);
|
2019-04-11 22:14:55 +02:00
|
|
|
}
|
|
|
|
|
2019-05-08 05:13:05 +02:00
|
|
|
void TextureCacheOpenGL::ImageBlit(Surface src_surface, Surface dst_surface,
|
|
|
|
const Common::Rectangle<u32>& src_rect,
|
|
|
|
const Common::Rectangle<u32>& dst_rect) {
|
|
|
|
const auto& src_params{src_surface->GetSurfaceParams()};
|
|
|
|
const auto& dst_params{dst_surface->GetSurfaceParams()};
|
|
|
|
|
|
|
|
OpenGLState prev_state{OpenGLState::GetCurState()};
|
|
|
|
SCOPE_EXIT({ prev_state.Apply(); });
|
|
|
|
|
|
|
|
OpenGLState state;
|
|
|
|
state.draw.read_framebuffer = src_framebuffer.handle;
|
|
|
|
state.draw.draw_framebuffer = dst_framebuffer.handle;
|
|
|
|
state.ApplyFramebufferState();
|
|
|
|
|
|
|
|
u32 buffers{};
|
|
|
|
|
|
|
|
UNIMPLEMENTED_IF(src_params.target != SurfaceTarget::Texture2D);
|
|
|
|
UNIMPLEMENTED_IF(dst_params.target != SurfaceTarget::Texture2D);
|
|
|
|
|
|
|
|
const GLuint src_texture{src_surface->GetTexture()};
|
|
|
|
const GLuint dst_texture{dst_surface->GetTexture()};
|
|
|
|
|
|
|
|
if (src_params.type == SurfaceType::ColorTexture) {
|
|
|
|
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
|
|
|
src_texture, 0);
|
|
|
|
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
|
|
|
|
0);
|
|
|
|
|
|
|
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
|
|
|
dst_texture, 0);
|
|
|
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
|
|
|
|
0);
|
|
|
|
|
|
|
|
buffers = GL_COLOR_BUFFER_BIT;
|
|
|
|
} else if (src_params.type == SurfaceType::Depth) {
|
|
|
|
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
|
|
|
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, src_texture,
|
|
|
|
0);
|
|
|
|
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, 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, dst_texture,
|
|
|
|
0);
|
|
|
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
|
|
|
|
|
|
|
|
buffers = GL_DEPTH_BUFFER_BIT;
|
|
|
|
} else if (src_params.type == 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,
|
|
|
|
src_texture, 0);
|
|
|
|
|
|
|
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
|
|
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
|
|
|
|
dst_texture, 0);
|
|
|
|
|
|
|
|
buffers = GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
|
|
|
|
}
|
|
|
|
|
|
|
|
glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom, dst_rect.left,
|
|
|
|
dst_rect.top, dst_rect.right, dst_rect.bottom, buffers,
|
|
|
|
buffers == GL_COLOR_BUFFER_BIT ? GL_LINEAR : GL_NEAREST);
|
|
|
|
}
|
|
|
|
|
2019-04-11 22:14:55 +02:00
|
|
|
} // namespace OpenGL
|