Compare commits
30 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
16ffecd8fb | ||
|
|
e8e5041955 | ||
|
|
17ad56c1dc | ||
|
|
f633b0c875 | ||
|
|
e3b6f6c016 | ||
|
|
412b31ad72 | ||
|
|
aa26baa3db | ||
|
|
4ef392906b | ||
|
|
3f49210234 | ||
|
|
fe84842137 | ||
|
|
5367935d35 | ||
|
|
8a47e7e493 | ||
|
|
e90a12f80c | ||
|
|
d019bb16f6 | ||
|
|
057170928c | ||
|
|
de18592179 | ||
|
|
60e6e8953e | ||
|
|
2985056340 | ||
|
|
ce4f159b1c | ||
|
|
6a999cf800 | ||
|
|
43d98ca8fe | ||
|
|
5b3fab6766 | ||
|
|
b2c1672e10 | ||
|
|
d3f9ea90e7 | ||
|
|
48d4efbd69 | ||
|
|
a3e82e8e1f | ||
|
|
ac09b5a2e9 | ||
|
|
6b63aaa5b4 | ||
|
|
db5f2bfa7e | ||
|
|
f600f6eebd |
@@ -7,7 +7,7 @@ yuzu is an experimental open-source emulator for the Nintendo Switch from the cr
|
||||
|
||||
It is written in C++ with portability in mind, with builds actively maintained for Windows, Linux and macOS. The emulator is currently only useful for homebrew development and research purposes.
|
||||
|
||||
yuzu only emulates a subset of Switch hardware and therefore is generally only useful for running/debugging homebrew applications. At this time, yuzu does not run any commercial Switch games. yuzu can boot some games, to varying degrees of success, but does not implement any of the necessary GPU features to render 3D graphics.
|
||||
yuzu only emulates a subset of Switch hardware and therefore is generally only useful for running/debugging homebrew applications. At this time, yuzu cannot play any commercial games without major problems. yuzu can boot some games, to varying degrees of success, but does not implement any of the necessary GPU features to render 3D graphics.
|
||||
|
||||
yuzu is licensed under the GPLv2 (or any later version). Refer to the license.txt file included.
|
||||
|
||||
|
||||
@@ -27,29 +27,23 @@
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#ifndef __func__
|
||||
#define __func__ __FUNCTION__
|
||||
#endif
|
||||
#endif
|
||||
using u8 = std::uint8_t; ///< 8-bit unsigned byte
|
||||
using u16 = std::uint16_t; ///< 16-bit unsigned short
|
||||
using u32 = std::uint32_t; ///< 32-bit unsigned word
|
||||
using u64 = std::uint64_t; ///< 64-bit unsigned int
|
||||
|
||||
typedef std::uint8_t u8; ///< 8-bit unsigned byte
|
||||
typedef std::uint16_t u16; ///< 16-bit unsigned short
|
||||
typedef std::uint32_t u32; ///< 32-bit unsigned word
|
||||
typedef std::uint64_t u64; ///< 64-bit unsigned int
|
||||
using s8 = std::int8_t; ///< 8-bit signed byte
|
||||
using s16 = std::int16_t; ///< 16-bit signed short
|
||||
using s32 = std::int32_t; ///< 32-bit signed word
|
||||
using s64 = std::int64_t; ///< 64-bit signed int
|
||||
|
||||
typedef std::int8_t s8; ///< 8-bit signed byte
|
||||
typedef std::int16_t s16; ///< 16-bit signed short
|
||||
typedef std::int32_t s32; ///< 32-bit signed word
|
||||
typedef std::int64_t s64; ///< 64-bit signed int
|
||||
|
||||
typedef float f32; ///< 32-bit floating point
|
||||
typedef double f64; ///< 64-bit floating point
|
||||
using f32 = float; ///< 32-bit floating point
|
||||
using f64 = double; ///< 64-bit floating point
|
||||
|
||||
// TODO: It would be nice to eventually replace these with strong types that prevent accidental
|
||||
// conversion between each other.
|
||||
typedef u64 VAddr; ///< Represents a pointer in the userspace virtual address space.
|
||||
typedef u64 PAddr; ///< Represents a pointer in the ARM11 physical address space.
|
||||
using VAddr = u64; ///< Represents a pointer in the userspace virtual address space.
|
||||
using PAddr = u64; ///< Represents a pointer in the ARM11 physical address space.
|
||||
|
||||
using u128 = std::array<std::uint64_t, 2>;
|
||||
static_assert(sizeof(u128) == 16, "u128 must be 128 bits wide");
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <algorithm>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
@@ -128,6 +129,8 @@ void NVFlinger::Compose() {
|
||||
// Search for a queued buffer and acquire it
|
||||
auto buffer = buffer_queue->AcquireBuffer();
|
||||
|
||||
MicroProfileFlip();
|
||||
|
||||
if (buffer == boost::none) {
|
||||
// There was no queued buffer to draw, render previous frame
|
||||
Core::System::GetInstance().perf_stats.EndGameFrame();
|
||||
|
||||
@@ -218,8 +218,9 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
|
||||
Texture::TICEntry tic_entry;
|
||||
Memory::ReadBlock(tic_address_cpu, &tic_entry, sizeof(Texture::TICEntry));
|
||||
|
||||
ASSERT_MSG(tic_entry.header_version == Texture::TICHeaderVersion::BlockLinear,
|
||||
"TIC versions other than BlockLinear are unimplemented");
|
||||
ASSERT_MSG(tic_entry.header_version == Texture::TICHeaderVersion::BlockLinear ||
|
||||
tic_entry.header_version == Texture::TICHeaderVersion::Pitch,
|
||||
"TIC versions other than BlockLinear or Pitch are unimplemented");
|
||||
|
||||
ASSERT_MSG((tic_entry.texture_type == Texture::TextureType::Texture2D) ||
|
||||
(tic_entry.texture_type == Texture::TextureType::Texture2DNoMipmap),
|
||||
|
||||
@@ -319,7 +319,15 @@ public:
|
||||
}
|
||||
} rt[NumRenderTargets];
|
||||
|
||||
INSERT_PADDING_WORDS(0x80);
|
||||
struct {
|
||||
f32 scale_x;
|
||||
f32 scale_y;
|
||||
f32 scale_z;
|
||||
u32 translate_x;
|
||||
u32 translate_y;
|
||||
u32 translate_z;
|
||||
INSERT_PADDING_WORDS(2);
|
||||
} viewport_transform[NumViewports];
|
||||
|
||||
struct {
|
||||
union {
|
||||
@@ -649,6 +657,7 @@ private:
|
||||
"Field " #field_name " has invalid position")
|
||||
|
||||
ASSERT_REG_POSITION(rt, 0x200);
|
||||
ASSERT_REG_POSITION(viewport_transform[0], 0x280);
|
||||
ASSERT_REG_POSITION(viewport, 0x300);
|
||||
ASSERT_REG_POSITION(vertex_buffer, 0x35D);
|
||||
ASSERT_REG_POSITION(zeta, 0x3F8);
|
||||
|
||||
@@ -90,6 +90,7 @@ union OpCode {
|
||||
enum class Id : u64 {
|
||||
TEXS = 0x6C,
|
||||
IPA = 0xE0,
|
||||
FMUL32_IMM = 0x1E,
|
||||
FFMA_IMM = 0x65,
|
||||
FFMA_CR = 0x93,
|
||||
FFMA_RC = 0xA3,
|
||||
@@ -142,6 +143,7 @@ union OpCode {
|
||||
|
||||
switch (op2) {
|
||||
case Id::IPA:
|
||||
case Id::FMUL32_IMM:
|
||||
return op2;
|
||||
}
|
||||
|
||||
@@ -235,6 +237,7 @@ union OpCode {
|
||||
info_table[Id::FMUL_R] = {Type::Arithmetic, "fmul_r"};
|
||||
info_table[Id::FMUL_C] = {Type::Arithmetic, "fmul_c"};
|
||||
info_table[Id::FMUL_IMM] = {Type::Arithmetic, "fmul_imm"};
|
||||
info_table[Id::FMUL32_IMM] = {Type::Arithmetic, "fmul32_imm"};
|
||||
info_table[Id::FSETP_C] = {Type::Arithmetic, "fsetp_c"};
|
||||
info_table[Id::FSETP_R] = {Type::Arithmetic, "fsetp_r"};
|
||||
info_table[Id::EXIT] = {Type::Trivial, "exit"};
|
||||
@@ -309,7 +312,8 @@ union Instruction {
|
||||
BitField<39, 8, Register> gpr39;
|
||||
|
||||
union {
|
||||
BitField<20, 19, u64> imm20;
|
||||
BitField<20, 19, u64> imm20_19;
|
||||
BitField<20, 32, u64> imm20_32;
|
||||
BitField<45, 1, u64> negate_b;
|
||||
BitField<46, 1, u64> abs_a;
|
||||
BitField<48, 1, u64> negate_a;
|
||||
@@ -317,14 +321,21 @@ union Instruction {
|
||||
BitField<50, 1, u64> abs_d;
|
||||
BitField<56, 1, u64> negate_imm;
|
||||
|
||||
float GetImm20() const {
|
||||
float GetImm20_19() const {
|
||||
float result{};
|
||||
u32 imm{static_cast<u32>(imm20)};
|
||||
u32 imm{static_cast<u32>(imm20_19)};
|
||||
imm <<= 12;
|
||||
imm |= negate_imm ? 0x80000000 : 0;
|
||||
std::memcpy(&result, &imm, sizeof(imm));
|
||||
return result;
|
||||
}
|
||||
|
||||
float GetImm20_32() const {
|
||||
float result{};
|
||||
u32 imm{static_cast<u32>(imm20_32)};
|
||||
std::memcpy(&result, &imm, sizeof(imm));
|
||||
return result;
|
||||
}
|
||||
} alu;
|
||||
|
||||
union {
|
||||
|
||||
@@ -523,9 +523,12 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& framebu
|
||||
src_params.width = std::min(framebuffer.width, pixel_stride);
|
||||
src_params.height = framebuffer.height;
|
||||
src_params.stride = pixel_stride;
|
||||
src_params.is_tiled = false;
|
||||
src_params.is_tiled = true;
|
||||
src_params.block_height = Tegra::Texture::TICEntry::DefaultBlockHeight;
|
||||
src_params.pixel_format =
|
||||
SurfaceParams::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format);
|
||||
src_params.component_type =
|
||||
SurfaceParams::ComponentTypeFromGPUPixelFormat(framebuffer.pixel_format);
|
||||
src_params.UpdateParams();
|
||||
|
||||
MathUtil::Rectangle<u32> src_rect;
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
|
||||
using SurfaceType = SurfaceParams::SurfaceType;
|
||||
using PixelFormat = SurfaceParams::PixelFormat;
|
||||
using ComponentType = SurfaceParams::ComponentType;
|
||||
|
||||
struct FormatTuple {
|
||||
GLint internal_format;
|
||||
@@ -47,26 +48,24 @@ struct FormatTuple {
|
||||
u32 compression_factor;
|
||||
};
|
||||
|
||||
static constexpr std::array<FormatTuple, 1> fb_format_tuples = {{
|
||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, false, 1}, // RGBA8
|
||||
static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_format_tuples = {{
|
||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, false, 1}, // ABGR8
|
||||
{GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV, false, 1}, // B5G6R5
|
||||
{GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, true, 16}, // DXT1
|
||||
{GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true, 16}, // DXT23
|
||||
{GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true, 16}, // DXT45
|
||||
}};
|
||||
|
||||
static constexpr std::array<FormatTuple, 2> tex_format_tuples = {{
|
||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, false, 1}, // RGBA8
|
||||
{GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, true, 16}, // DXT1
|
||||
}};
|
||||
|
||||
static const FormatTuple& GetFormatTuple(PixelFormat pixel_format) {
|
||||
static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType component_type) {
|
||||
const SurfaceType type = SurfaceParams::GetFormatType(pixel_format);
|
||||
if (type == SurfaceType::Color) {
|
||||
ASSERT(static_cast<size_t>(pixel_format) < fb_format_tuples.size());
|
||||
return fb_format_tuples[static_cast<unsigned int>(pixel_format)];
|
||||
if (type == SurfaceType::ColorTexture) {
|
||||
ASSERT(static_cast<size_t>(pixel_format) < tex_format_tuples.size());
|
||||
// For now only UNORM components are supported
|
||||
ASSERT(component_type == ComponentType::UNorm);
|
||||
return tex_format_tuples[static_cast<unsigned int>(pixel_format)];
|
||||
} else if (type == SurfaceType::Depth || type == SurfaceType::DepthStencil) {
|
||||
// TODO(Subv): Implement depth formats
|
||||
ASSERT_MSG(false, "Unimplemented");
|
||||
} else if (type == SurfaceType::Texture) {
|
||||
ASSERT(static_cast<size_t>(pixel_format) < tex_format_tuples.size());
|
||||
return tex_format_tuples[static_cast<unsigned int>(pixel_format)];
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
@@ -85,56 +84,42 @@ static u16 GetResolutionScaleFactor() {
|
||||
}
|
||||
|
||||
template <bool morton_to_gl, PixelFormat format>
|
||||
static void MortonCopyTile(u32 stride, u8* tile_buffer, u8* gl_buffer) {
|
||||
void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, VAddr base, VAddr start,
|
||||
VAddr end) {
|
||||
constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / 8;
|
||||
constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format);
|
||||
for (u32 y = 0; y < 8; ++y) {
|
||||
for (u32 x = 0; x < 8; ++x) {
|
||||
u8* tile_ptr = tile_buffer + VideoCore::MortonInterleave(x, y) * bytes_per_pixel;
|
||||
u8* gl_ptr = gl_buffer + ((7 - y) * stride + x) * gl_bytes_per_pixel;
|
||||
if (morton_to_gl) {
|
||||
std::memcpy(gl_ptr, tile_ptr, bytes_per_pixel);
|
||||
} else {
|
||||
std::memcpy(tile_ptr, gl_ptr, bytes_per_pixel);
|
||||
}
|
||||
}
|
||||
|
||||
if (morton_to_gl) {
|
||||
auto data = Tegra::Texture::UnswizzleTexture(
|
||||
base, SurfaceParams::TextureFormatFromPixelFormat(format), stride, height,
|
||||
block_height);
|
||||
std::memcpy(gl_buffer, data.data(), data.size());
|
||||
} else {
|
||||
// TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should check
|
||||
// the configuration for this and perform more generic un/swizzle
|
||||
LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!");
|
||||
VideoCore::MortonCopyPixels128(stride, height, bytes_per_pixel, gl_bytes_per_pixel,
|
||||
Memory::GetPointer(base), gl_buffer, morton_to_gl);
|
||||
}
|
||||
}
|
||||
|
||||
template <bool morton_to_gl, PixelFormat format>
|
||||
void MortonCopy(u32 stride, u32 height, u8* gl_buffer, VAddr base, VAddr start, VAddr end) {
|
||||
constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / 8;
|
||||
constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format);
|
||||
|
||||
// TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should check the
|
||||
// configuration for this and perform more generic un/swizzle
|
||||
LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!");
|
||||
VideoCore::MortonCopyPixels128(stride, height, bytes_per_pixel, gl_bytes_per_pixel,
|
||||
Memory::GetPointer(base), gl_buffer, morton_to_gl);
|
||||
}
|
||||
|
||||
template <>
|
||||
void MortonCopy<true, PixelFormat::DXT1>(u32 stride, u32 height, u8* gl_buffer, VAddr base,
|
||||
VAddr start, VAddr end) {
|
||||
constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(PixelFormat::DXT1) / 8;
|
||||
constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(PixelFormat::DXT1);
|
||||
|
||||
// TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should check the
|
||||
// configuration for this and perform more generic un/swizzle
|
||||
LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!");
|
||||
auto data =
|
||||
Tegra::Texture::UnswizzleTexture(base, Tegra::Texture::TextureFormat::DXT1, stride, height);
|
||||
std::memcpy(gl_buffer, data.data(), data.size());
|
||||
}
|
||||
|
||||
static constexpr std::array<void (*)(u32, u32, u8*, VAddr, VAddr, VAddr), 2> morton_to_gl_fns = {
|
||||
MortonCopy<true, PixelFormat::RGBA8>,
|
||||
MortonCopy<true, PixelFormat::DXT1>,
|
||||
static constexpr std::array<void (*)(u32, u32, u32, u8*, VAddr, VAddr, VAddr),
|
||||
SurfaceParams::MaxPixelFormat>
|
||||
morton_to_gl_fns = {
|
||||
MortonCopy<true, PixelFormat::ABGR8>, MortonCopy<true, PixelFormat::B5G6R5>,
|
||||
MortonCopy<true, PixelFormat::DXT1>, MortonCopy<true, PixelFormat::DXT23>,
|
||||
MortonCopy<true, PixelFormat::DXT45>,
|
||||
};
|
||||
|
||||
static constexpr std::array<void (*)(u32, u32, u8*, VAddr, VAddr, VAddr), 2> gl_to_morton_fns = {
|
||||
MortonCopy<false, PixelFormat::RGBA8>,
|
||||
MortonCopy<false, PixelFormat::DXT1>,
|
||||
static constexpr std::array<void (*)(u32, u32, u32, u8*, VAddr, VAddr, VAddr),
|
||||
SurfaceParams::MaxPixelFormat>
|
||||
gl_to_morton_fns = {
|
||||
MortonCopy<false, PixelFormat::ABGR8>,
|
||||
MortonCopy<false, PixelFormat::B5G6R5>,
|
||||
// TODO(Subv): Swizzling the DXT1/DXT23/DXT45 formats is not yet supported
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
};
|
||||
|
||||
// Allocate an uninitialized texture of appropriate size and format for the surface
|
||||
@@ -183,7 +168,7 @@ static bool BlitTextures(GLuint src_tex, const MathUtil::Rectangle<u32>& src_rec
|
||||
|
||||
u32 buffers = 0;
|
||||
|
||||
if (type == SurfaceType::Color || type == SurfaceType::Texture) {
|
||||
if (type == SurfaceType::ColorTexture) {
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, src_tex,
|
||||
0);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
|
||||
@@ -311,15 +296,18 @@ MathUtil::Rectangle<u32> SurfaceParams::GetScaledSubRect(const SurfaceParams& su
|
||||
|
||||
bool SurfaceParams::ExactMatch(const SurfaceParams& other_surface) const {
|
||||
return std::tie(other_surface.addr, other_surface.width, other_surface.height,
|
||||
other_surface.stride, other_surface.pixel_format, other_surface.is_tiled) ==
|
||||
std::tie(addr, width, height, stride, pixel_format, is_tiled) &&
|
||||
other_surface.stride, other_surface.block_height, other_surface.pixel_format,
|
||||
other_surface.component_type,
|
||||
other_surface.is_tiled) == std::tie(addr, width, height, stride, block_height,
|
||||
pixel_format, component_type, is_tiled) &&
|
||||
pixel_format != PixelFormat::Invalid;
|
||||
}
|
||||
|
||||
bool SurfaceParams::CanSubRect(const SurfaceParams& sub_surface) const {
|
||||
return sub_surface.addr >= addr && sub_surface.end <= end &&
|
||||
sub_surface.pixel_format == pixel_format && pixel_format != PixelFormat::Invalid &&
|
||||
sub_surface.is_tiled == is_tiled &&
|
||||
sub_surface.is_tiled == is_tiled && sub_surface.block_height == block_height &&
|
||||
sub_surface.component_type == component_type &&
|
||||
(sub_surface.addr - addr) % BytesInPixels(is_tiled ? 64 : 1) == 0 &&
|
||||
(sub_surface.stride == stride || sub_surface.height <= (is_tiled ? 8u : 1u)) &&
|
||||
GetSubRect(sub_surface).left + sub_surface.width <= stride;
|
||||
@@ -328,7 +316,8 @@ bool SurfaceParams::CanSubRect(const SurfaceParams& sub_surface) const {
|
||||
bool SurfaceParams::CanExpand(const SurfaceParams& expanded_surface) const {
|
||||
return pixel_format != PixelFormat::Invalid && pixel_format == expanded_surface.pixel_format &&
|
||||
addr <= expanded_surface.end && expanded_surface.addr <= end &&
|
||||
is_tiled == expanded_surface.is_tiled && stride == expanded_surface.stride &&
|
||||
is_tiled == expanded_surface.is_tiled && block_height == expanded_surface.block_height &&
|
||||
component_type == expanded_surface.component_type && stride == expanded_surface.stride &&
|
||||
(std::max(expanded_surface.addr, addr) - std::min(expanded_surface.addr, addr)) %
|
||||
BytesInPixels(stride * (is_tiled ? 8 : 1)) ==
|
||||
0;
|
||||
@@ -339,6 +328,10 @@ bool SurfaceParams::CanTexCopy(const SurfaceParams& texcopy_params) const {
|
||||
end < texcopy_params.end) {
|
||||
return false;
|
||||
}
|
||||
if (texcopy_params.block_height != block_height ||
|
||||
texcopy_params.component_type != component_type)
|
||||
return false;
|
||||
|
||||
if (texcopy_params.width != texcopy_params.stride) {
|
||||
const u32 tile_stride = static_cast<u32>(BytesInPixels(stride * (is_tiled ? 8 : 1)));
|
||||
return (texcopy_params.addr - addr) % BytesInPixels(is_tiled ? 64 : 1) == 0 &&
|
||||
@@ -481,18 +474,13 @@ void CachedSurface::LoadGLBuffer(VAddr load_start, VAddr load_end) {
|
||||
const u64 start_offset = load_start - addr;
|
||||
|
||||
if (!is_tiled) {
|
||||
ASSERT(type == SurfaceType::Color);
|
||||
const u32 bytes_per_pixel{GetFormatBpp() >> 3};
|
||||
|
||||
// TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should check
|
||||
// the configuration for this and perform more generic un/swizzle
|
||||
LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!");
|
||||
VideoCore::MortonCopyPixels128(width, height, bytes_per_pixel, 4,
|
||||
texture_src_data + start_offset, &gl_buffer[start_offset],
|
||||
true);
|
||||
std::memcpy(&gl_buffer[start_offset], texture_src_data + start_offset,
|
||||
bytes_per_pixel * width * height);
|
||||
} else {
|
||||
morton_to_gl_fns[static_cast<size_t>(pixel_format)](stride, height, &gl_buffer[0], addr,
|
||||
load_start, load_end);
|
||||
morton_to_gl_fns[static_cast<size_t>(pixel_format)](
|
||||
stride, block_height, height, &gl_buffer[0], addr, load_start, load_end);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -533,11 +521,10 @@ void CachedSurface::FlushGLBuffer(VAddr flush_start, VAddr flush_end) {
|
||||
if (backup_bytes)
|
||||
std::memcpy(&dst_buffer[coarse_start_offset], &backup_data[0], backup_bytes);
|
||||
} else if (!is_tiled) {
|
||||
ASSERT(type == SurfaceType::Color);
|
||||
std::memcpy(dst_buffer + start_offset, &gl_buffer[start_offset], flush_end - flush_start);
|
||||
} else {
|
||||
gl_to_morton_fns[static_cast<size_t>(pixel_format)](stride, height, &gl_buffer[0], addr,
|
||||
flush_start, flush_end);
|
||||
gl_to_morton_fns[static_cast<size_t>(pixel_format)](
|
||||
stride, block_height, height, &gl_buffer[0], addr, flush_start, flush_end);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -556,7 +543,7 @@ void CachedSurface::UploadGLTexture(const MathUtil::Rectangle<u32>& rect, GLuint
|
||||
GLint y0 = static_cast<GLint>(rect.bottom);
|
||||
size_t buffer_offset = (y0 * stride + x0) * GetGLBytesPerPixel(pixel_format);
|
||||
|
||||
const FormatTuple& tuple = GetFormatTuple(pixel_format);
|
||||
const FormatTuple& tuple = GetFormatTuple(pixel_format, component_type);
|
||||
GLuint target_tex = texture.handle;
|
||||
|
||||
// If not 1x scale, create 1x texture that we will blit from to replace texture subrect in
|
||||
@@ -629,7 +616,7 @@ void CachedSurface::DownloadGLTexture(const MathUtil::Rectangle<u32>& rect, GLui
|
||||
OpenGLState prev_state = state;
|
||||
SCOPE_EXIT({ prev_state.Apply(); });
|
||||
|
||||
const FormatTuple& tuple = GetFormatTuple(pixel_format);
|
||||
const FormatTuple& tuple = GetFormatTuple(pixel_format, component_type);
|
||||
|
||||
// Ensure no bad interactions with GL_PACK_ALIGNMENT
|
||||
ASSERT(stride * GetGLBytesPerPixel(pixel_format) % 4 == 0);
|
||||
@@ -662,7 +649,7 @@ void CachedSurface::DownloadGLTexture(const MathUtil::Rectangle<u32>& rect, GLui
|
||||
state.draw.read_framebuffer = read_fb_handle;
|
||||
state.Apply();
|
||||
|
||||
if (type == SurfaceType::Color || type == SurfaceType::Texture) {
|
||||
if (type == SurfaceType::ColorTexture) {
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
texture.handle, 0);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
|
||||
@@ -1041,9 +1028,25 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextu
|
||||
params.height = config.tic.Height();
|
||||
params.is_tiled = config.tic.IsTiled();
|
||||
params.pixel_format = SurfaceParams::PixelFormatFromTextureFormat(config.tic.format);
|
||||
|
||||
// TODO(Subv): Different types per component are not supported.
|
||||
ASSERT(config.tic.r_type.Value() == config.tic.g_type.Value() &&
|
||||
config.tic.r_type.Value() == config.tic.b_type.Value() &&
|
||||
config.tic.r_type.Value() == config.tic.a_type.Value());
|
||||
|
||||
params.component_type = SurfaceParams::ComponentTypeFromTexture(config.tic.r_type.Value());
|
||||
|
||||
if (config.tic.IsTiled()) {
|
||||
params.block_height = config.tic.BlockHeight();
|
||||
} else {
|
||||
// Use the texture-provided stride value if the texture isn't tiled.
|
||||
params.stride = params.PixelsInBytes(config.tic.Pitch());
|
||||
}
|
||||
|
||||
params.UpdateParams();
|
||||
|
||||
if (config.tic.Width() % 8 != 0 || config.tic.Height() % 8 != 0) {
|
||||
if (config.tic.Width() % 8 != 0 || config.tic.Height() % 8 != 0 ||
|
||||
params.stride != params.width) {
|
||||
Surface src_surface;
|
||||
MathUtil::Rectangle<u32> rect;
|
||||
std::tie(src_surface, rect) = GetSurfaceSubRect(params, ScaleMatch::Ignore, true);
|
||||
@@ -1094,10 +1097,13 @@ SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces(
|
||||
color_params.res_scale = resolution_scale_factor;
|
||||
color_params.width = config.width;
|
||||
color_params.height = config.height;
|
||||
// TODO(Subv): Can framebuffers use a different block height?
|
||||
color_params.block_height = Tegra::Texture::TICEntry::DefaultBlockHeight;
|
||||
SurfaceParams depth_params = color_params;
|
||||
|
||||
color_params.addr = memory_manager->PhysicalToVirtualAddress(config.Address());
|
||||
color_params.pixel_format = SurfaceParams::PixelFormatFromRenderTargetFormat(config.format);
|
||||
color_params.component_type = SurfaceParams::ComponentTypeFromRenderTarget(config.format);
|
||||
color_params.UpdateParams();
|
||||
|
||||
ASSERT_MSG(!using_depth_fb, "depth buffer is unimplemented");
|
||||
@@ -1293,7 +1299,6 @@ void RasterizerCacheOpenGL::InvalidateRegion(VAddr addr, u64 size, const Surface
|
||||
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);
|
||||
// Surfaces can't have a gap
|
||||
ASSERT(region_owner->width == region_owner->stride);
|
||||
@@ -1355,7 +1360,8 @@ Surface RasterizerCacheOpenGL::CreateSurface(const SurfaceParams& params) {
|
||||
|
||||
surface->gl_buffer_size = 0;
|
||||
surface->invalid_regions.insert(surface->GetInterval());
|
||||
AllocateSurfaceTexture(surface->texture.handle, GetFormatTuple(surface->pixel_format),
|
||||
AllocateSurfaceTexture(surface->texture.handle,
|
||||
GetFormatTuple(surface->pixel_format, surface->component_type),
|
||||
surface->GetScaledWidth(), surface->GetScaledHeight());
|
||||
|
||||
return surface;
|
||||
|
||||
@@ -52,27 +52,45 @@ enum class ScaleMatch {
|
||||
|
||||
struct SurfaceParams {
|
||||
enum class PixelFormat {
|
||||
RGBA8 = 0,
|
||||
DXT1 = 1,
|
||||
ABGR8 = 0,
|
||||
B5G6R5 = 1,
|
||||
DXT1 = 2,
|
||||
DXT23 = 3,
|
||||
DXT45 = 4,
|
||||
|
||||
Max,
|
||||
Invalid = 255,
|
||||
};
|
||||
|
||||
static constexpr size_t MaxPixelFormat = static_cast<size_t>(PixelFormat::Max);
|
||||
|
||||
enum class ComponentType {
|
||||
Invalid = 0,
|
||||
SNorm = 1,
|
||||
UNorm = 2,
|
||||
SInt = 3,
|
||||
UInt = 4,
|
||||
Float = 5,
|
||||
};
|
||||
|
||||
enum class SurfaceType {
|
||||
Color = 0,
|
||||
Texture = 1,
|
||||
Depth = 2,
|
||||
DepthStencil = 3,
|
||||
Fill = 4,
|
||||
Invalid = 5
|
||||
ColorTexture = 0,
|
||||
Depth = 1,
|
||||
DepthStencil = 2,
|
||||
Fill = 3,
|
||||
Invalid = 4,
|
||||
};
|
||||
|
||||
static constexpr unsigned int GetFormatBpp(PixelFormat format) {
|
||||
if (format == PixelFormat::Invalid)
|
||||
return 0;
|
||||
|
||||
constexpr std::array<unsigned int, 2> bpp_table = {
|
||||
32, // RGBA8
|
||||
64, // DXT1
|
||||
constexpr std::array<unsigned int, MaxPixelFormat> bpp_table = {
|
||||
32, // ABGR8
|
||||
16, // B5G6R5
|
||||
64, // DXT1
|
||||
128, // DXT23
|
||||
128, // DXT45
|
||||
};
|
||||
|
||||
ASSERT(static_cast<size_t>(format) < bpp_table.size());
|
||||
@@ -85,7 +103,7 @@ struct SurfaceParams {
|
||||
static PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format) {
|
||||
switch (format) {
|
||||
case Tegra::RenderTargetFormat::RGBA8_UNORM:
|
||||
return PixelFormat::RGBA8;
|
||||
return PixelFormat::ABGR8;
|
||||
default:
|
||||
NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
|
||||
UNREACHABLE();
|
||||
@@ -95,7 +113,7 @@ struct SurfaceParams {
|
||||
static PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format) {
|
||||
switch (format) {
|
||||
case Tegra::FramebufferConfig::PixelFormat::ABGR8:
|
||||
return PixelFormat::RGBA8;
|
||||
return PixelFormat::ABGR8;
|
||||
default:
|
||||
NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
|
||||
UNREACHABLE();
|
||||
@@ -106,9 +124,67 @@ struct SurfaceParams {
|
||||
// TODO(Subv): Properly implement this
|
||||
switch (format) {
|
||||
case Tegra::Texture::TextureFormat::A8R8G8B8:
|
||||
return PixelFormat::RGBA8;
|
||||
return PixelFormat::ABGR8;
|
||||
case Tegra::Texture::TextureFormat::B5G6R5:
|
||||
return PixelFormat::B5G6R5;
|
||||
case Tegra::Texture::TextureFormat::DXT1:
|
||||
return PixelFormat::DXT1;
|
||||
case Tegra::Texture::TextureFormat::DXT23:
|
||||
return PixelFormat::DXT23;
|
||||
case Tegra::Texture::TextureFormat::DXT45:
|
||||
return PixelFormat::DXT45;
|
||||
default:
|
||||
NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
static Tegra::Texture::TextureFormat TextureFormatFromPixelFormat(PixelFormat format) {
|
||||
// TODO(Subv): Properly implement this
|
||||
switch (format) {
|
||||
case PixelFormat::ABGR8:
|
||||
return Tegra::Texture::TextureFormat::A8R8G8B8;
|
||||
case PixelFormat::B5G6R5:
|
||||
return Tegra::Texture::TextureFormat::B5G6R5;
|
||||
case PixelFormat::DXT1:
|
||||
return Tegra::Texture::TextureFormat::DXT1;
|
||||
case PixelFormat::DXT23:
|
||||
return Tegra::Texture::TextureFormat::DXT23;
|
||||
case PixelFormat::DXT45:
|
||||
return Tegra::Texture::TextureFormat::DXT45;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
static ComponentType ComponentTypeFromTexture(Tegra::Texture::ComponentType type) {
|
||||
// TODO(Subv): Implement more component types
|
||||
switch (type) {
|
||||
case Tegra::Texture::ComponentType::UNORM:
|
||||
return ComponentType::UNorm;
|
||||
default:
|
||||
NGLOG_CRITICAL(HW_GPU, "Unimplemented component type={}", static_cast<u32>(type));
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
static ComponentType ComponentTypeFromRenderTarget(Tegra::RenderTargetFormat format) {
|
||||
// TODO(Subv): Implement more render targets
|
||||
switch (format) {
|
||||
case Tegra::RenderTargetFormat::RGBA8_UNORM:
|
||||
case Tegra::RenderTargetFormat::RGB10_A2_UNORM:
|
||||
return ComponentType::UNorm;
|
||||
default:
|
||||
NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
static ComponentType ComponentTypeFromGPUPixelFormat(
|
||||
Tegra::FramebufferConfig::PixelFormat format) {
|
||||
switch (format) {
|
||||
case Tegra::FramebufferConfig::PixelFormat::ABGR8:
|
||||
return ComponentType::UNorm;
|
||||
default:
|
||||
NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
|
||||
UNREACHABLE();
|
||||
@@ -119,8 +195,7 @@ struct SurfaceParams {
|
||||
SurfaceType a_type = GetFormatType(pixel_format_a);
|
||||
SurfaceType b_type = GetFormatType(pixel_format_b);
|
||||
|
||||
if ((a_type == SurfaceType::Color || a_type == SurfaceType::Texture) &&
|
||||
(b_type == SurfaceType::Color || b_type == SurfaceType::Texture)) {
|
||||
if (a_type == SurfaceType::ColorTexture && b_type == SurfaceType::ColorTexture) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -136,12 +211,8 @@ struct SurfaceParams {
|
||||
}
|
||||
|
||||
static SurfaceType GetFormatType(PixelFormat pixel_format) {
|
||||
if ((unsigned int)pixel_format <= static_cast<unsigned int>(PixelFormat::RGBA8)) {
|
||||
return SurfaceType::Color;
|
||||
}
|
||||
|
||||
if ((unsigned int)pixel_format <= static_cast<unsigned int>(PixelFormat::DXT1)) {
|
||||
return SurfaceType::Texture;
|
||||
if (static_cast<size_t>(pixel_format) < MaxPixelFormat) {
|
||||
return SurfaceType::ColorTexture;
|
||||
}
|
||||
|
||||
// TODO(Subv): Implement the other formats
|
||||
@@ -213,11 +284,13 @@ struct SurfaceParams {
|
||||
u32 width = 0;
|
||||
u32 height = 0;
|
||||
u32 stride = 0;
|
||||
u32 block_height = 0;
|
||||
u16 res_scale = 1;
|
||||
|
||||
bool is_tiled = false;
|
||||
PixelFormat pixel_format = PixelFormat::Invalid;
|
||||
SurfaceType type = SurfaceType::Invalid;
|
||||
ComponentType component_type = ComponentType::Invalid;
|
||||
};
|
||||
|
||||
struct CachedSurface : SurfaceParams {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
@@ -109,12 +110,25 @@ private:
|
||||
|
||||
class ShaderWriter {
|
||||
public:
|
||||
void AddLine(const std::string& text) {
|
||||
void AddLine(std::string_view text) {
|
||||
DEBUG_ASSERT(scope >= 0);
|
||||
if (!text.empty()) {
|
||||
shader_source += std::string(static_cast<size_t>(scope) * 4, ' ');
|
||||
AppendIndentation();
|
||||
}
|
||||
shader_source += text + '\n';
|
||||
shader_source += text;
|
||||
AddNewLine();
|
||||
}
|
||||
|
||||
void AddLine(char character) {
|
||||
DEBUG_ASSERT(scope >= 0);
|
||||
AppendIndentation();
|
||||
shader_source += character;
|
||||
AddNewLine();
|
||||
}
|
||||
|
||||
void AddNewLine() {
|
||||
DEBUG_ASSERT(scope >= 0);
|
||||
shader_source += '\n';
|
||||
}
|
||||
|
||||
std::string GetResult() {
|
||||
@@ -124,6 +138,10 @@ public:
|
||||
int scope = 0;
|
||||
|
||||
private:
|
||||
void AppendIndentation() {
|
||||
shader_source.append(static_cast<size_t>(scope) * 4, ' ');
|
||||
}
|
||||
|
||||
std::string shader_source;
|
||||
};
|
||||
|
||||
@@ -190,9 +208,14 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates code representing an immediate value
|
||||
static std::string GetImmediate(const Instruction& instr) {
|
||||
return std::to_string(instr.alu.GetImm20());
|
||||
/// Generates code representing a 19-bit immediate value
|
||||
static std::string GetImmediate19(const Instruction& instr) {
|
||||
return std::to_string(instr.alu.GetImm20_19());
|
||||
}
|
||||
|
||||
/// Generates code representing a 32-bit immediate value
|
||||
static std::string GetImmediate32(const Instruction& instr) {
|
||||
return std::to_string(instr.alu.GetImm20_32());
|
||||
}
|
||||
|
||||
/// Generates code representing a temporary (GPR) register.
|
||||
@@ -276,7 +299,7 @@ private:
|
||||
std::string op_b = instr.alu.negate_b ? "-" : "";
|
||||
|
||||
if (instr.is_b_imm) {
|
||||
op_b += GetImmediate(instr);
|
||||
op_b += GetImmediate19(instr);
|
||||
} else {
|
||||
if (instr.is_b_gpr) {
|
||||
op_b += GetRegister(instr.gpr20);
|
||||
@@ -296,6 +319,11 @@ private:
|
||||
SetDest(0, dest, op_a + " * " + op_b, 1, 1, instr.alu.abs_d);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::FMUL32_IMM: {
|
||||
// fmul32i doesn't have abs or neg bits.
|
||||
SetDest(0, dest, GetRegister(instr.gpr8) + " * " + GetImmediate32(instr), 1, 1);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::FADD_C:
|
||||
case OpCode::Id::FADD_R:
|
||||
case OpCode::Id::FADD_IMM: {
|
||||
@@ -364,7 +392,7 @@ private:
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::FFMA_IMM: {
|
||||
op_b += GetImmediate(instr);
|
||||
op_b += GetImmediate19(instr);
|
||||
op_c += GetRegister(instr.gpr39);
|
||||
break;
|
||||
}
|
||||
@@ -399,11 +427,18 @@ private:
|
||||
const std::string op_a = GetRegister(instr.gpr8);
|
||||
const std::string op_b = GetRegister(instr.gpr20);
|
||||
const std::string sampler = GetSampler(instr.sampler);
|
||||
const std::string coord = "vec2(" + op_a + ", " + op_b + ")";
|
||||
const std::string texture = "texture(" + sampler + ", " + coord + ")";
|
||||
const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");";
|
||||
// Add an extra scope and declare the texture coords inside to prevent overwriting
|
||||
// them in case they are used as outputs of the texs instruction.
|
||||
shader.AddLine("{");
|
||||
++shader.scope;
|
||||
shader.AddLine(coord);
|
||||
const std::string texture = "texture(" + sampler + ", coords)";
|
||||
for (unsigned elem = 0; elem < instr.attribute.fmt20.size; ++elem) {
|
||||
SetDest(elem, GetRegister(instr.gpr0, elem), texture, 1, 4);
|
||||
}
|
||||
--shader.scope;
|
||||
shader.AddLine("}");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@@ -464,7 +499,7 @@ private:
|
||||
for (const auto& subroutine : subroutines) {
|
||||
shader.AddLine("bool " + subroutine.GetName() + "();");
|
||||
}
|
||||
shader.AddLine("");
|
||||
shader.AddNewLine();
|
||||
|
||||
// Add the main entry point
|
||||
shader.AddLine("bool exec_shader() {");
|
||||
@@ -507,14 +542,14 @@ private:
|
||||
}
|
||||
|
||||
--shader.scope;
|
||||
shader.AddLine("}");
|
||||
shader.AddLine('}');
|
||||
}
|
||||
|
||||
shader.AddLine("default: return false;");
|
||||
shader.AddLine("}");
|
||||
shader.AddLine('}');
|
||||
|
||||
--shader.scope;
|
||||
shader.AddLine("}");
|
||||
shader.AddLine('}');
|
||||
|
||||
shader.AddLine("return false;");
|
||||
}
|
||||
@@ -541,7 +576,7 @@ private:
|
||||
for (const auto& reg : declr_register) {
|
||||
declarations.AddLine("float " + reg + " = 0.0;");
|
||||
}
|
||||
declarations.AddLine("");
|
||||
declarations.AddNewLine();
|
||||
|
||||
for (const auto& index : declr_input_attribute) {
|
||||
// TODO(bunnei): Use proper number of elements for these
|
||||
@@ -550,7 +585,7 @@ private:
|
||||
static_cast<u32>(Attribute::Index::Attribute_0)) +
|
||||
") in vec4 " + GetInputAttribute(index) + ";");
|
||||
}
|
||||
declarations.AddLine("");
|
||||
declarations.AddNewLine();
|
||||
|
||||
for (const auto& index : declr_output_attribute) {
|
||||
// TODO(bunnei): Use proper number of elements for these
|
||||
@@ -559,15 +594,15 @@ private:
|
||||
static_cast<u32>(Attribute::Index::Attribute_0)) +
|
||||
") out vec4 " + GetOutputAttribute(index) + ";");
|
||||
}
|
||||
declarations.AddLine("");
|
||||
declarations.AddNewLine();
|
||||
|
||||
unsigned const_buffer_layout = 0;
|
||||
for (const auto& entry : GetConstBuffersDeclarations()) {
|
||||
declarations.AddLine("layout(std430) buffer " + entry.GetName());
|
||||
declarations.AddLine("{");
|
||||
declarations.AddLine('{');
|
||||
declarations.AddLine(" float c" + std::to_string(entry.GetIndex()) + "[];");
|
||||
declarations.AddLine("};");
|
||||
declarations.AddLine("");
|
||||
declarations.AddNewLine();
|
||||
++const_buffer_layout;
|
||||
}
|
||||
}
|
||||
@@ -586,7 +621,7 @@ private:
|
||||
std::set<Attribute::Index> declr_input_attribute;
|
||||
std::set<Attribute::Index> declr_output_attribute;
|
||||
std::array<ConstBufferEntry, Maxwell3D::Regs::MaxConstBuffers> declr_const_buffers;
|
||||
};
|
||||
}; // namespace Decompiler
|
||||
|
||||
std::string GetCommonDeclarations() {
|
||||
return "bool exec_shader();";
|
||||
|
||||
@@ -29,9 +29,15 @@ out gl_PerVertex {
|
||||
|
||||
out vec4 position;
|
||||
|
||||
layout (std140) uniform vs_config {
|
||||
vec4 viewport_flip;
|
||||
};
|
||||
|
||||
void main() {
|
||||
exec_shader();
|
||||
|
||||
// Viewport can be flipped, which is unsupported by glViewport
|
||||
position.xy *= viewport_flip.xy;
|
||||
gl_Position = position;
|
||||
}
|
||||
)";
|
||||
@@ -52,6 +58,10 @@ ProgramResult GenerateFragmentShader(const ShaderSetup& setup, const MaxwellFSCo
|
||||
in vec4 position;
|
||||
out vec4 color;
|
||||
|
||||
layout (std140) uniform fs_config {
|
||||
vec4 viewport_flip;
|
||||
};
|
||||
|
||||
uniform sampler2D tex[32];
|
||||
|
||||
void main() {
|
||||
|
||||
@@ -53,6 +53,12 @@ void SetShaderSamplerBindings(GLuint shader) {
|
||||
|
||||
} // namespace Impl
|
||||
|
||||
void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage) {}
|
||||
void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage) {
|
||||
const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
|
||||
|
||||
// TODO(bunnei): Support more than one viewport
|
||||
viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0 : 1.0;
|
||||
viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0 : 1.0;
|
||||
}
|
||||
|
||||
} // namespace GLShader
|
||||
|
||||
@@ -30,10 +30,9 @@ void SetShaderSamplerBindings(GLuint shader);
|
||||
// Not following that rule will cause problems on some AMD drivers.
|
||||
struct MaxwellUniformData {
|
||||
void SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage);
|
||||
// TODO(Subv): Use this for something.
|
||||
alignas(16) GLvec4 viewport_flip;
|
||||
};
|
||||
// static_assert(sizeof(MaxwellUniformData) == 1024, "MaxwellUniformData structure size is
|
||||
// incorrect");
|
||||
static_assert(sizeof(MaxwellUniformData) == 16, "MaxwellUniformData structure size is incorrect");
|
||||
static_assert(sizeof(MaxwellUniformData) < 16384,
|
||||
"MaxwellUniformData structure must be less than 16kb as per the OpenGL spec");
|
||||
|
||||
|
||||
@@ -48,31 +48,39 @@ u32 BytesPerPixel(TextureFormat format) {
|
||||
case TextureFormat::DXT1:
|
||||
// In this case a 'pixel' actually refers to a 4x4 tile.
|
||||
return 8;
|
||||
case TextureFormat::DXT23:
|
||||
case TextureFormat::DXT45:
|
||||
// In this case a 'pixel' actually refers to a 4x4 tile.
|
||||
return 16;
|
||||
case TextureFormat::A8R8G8B8:
|
||||
return 4;
|
||||
case TextureFormat::B5G6R5:
|
||||
return 2;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Format not implemented");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width, u32 height) {
|
||||
std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width, u32 height,
|
||||
u32 block_height) {
|
||||
u8* data = Memory::GetPointer(address);
|
||||
u32 bytes_per_pixel = BytesPerPixel(format);
|
||||
|
||||
static constexpr u32 DefaultBlockHeight = 16;
|
||||
|
||||
std::vector<u8> unswizzled_data(width * height * bytes_per_pixel);
|
||||
|
||||
switch (format) {
|
||||
case TextureFormat::DXT1:
|
||||
// In the DXT1 format, each 4x4 tile is swizzled instead of just individual pixel values.
|
||||
case TextureFormat::DXT23:
|
||||
case TextureFormat::DXT45:
|
||||
// In the DXT formats, each 4x4 tile is swizzled instead of just individual pixel values.
|
||||
CopySwizzledData(width / 4, height / 4, bytes_per_pixel, bytes_per_pixel, data,
|
||||
unswizzled_data.data(), true, DefaultBlockHeight);
|
||||
unswizzled_data.data(), true, block_height);
|
||||
break;
|
||||
case TextureFormat::A8R8G8B8:
|
||||
case TextureFormat::B5G6R5:
|
||||
CopySwizzledData(width, height, bytes_per_pixel, bytes_per_pixel, data,
|
||||
unswizzled_data.data(), true, DefaultBlockHeight);
|
||||
unswizzled_data.data(), true, block_height);
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Format not implemented");
|
||||
@@ -89,7 +97,10 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
|
||||
// TODO(Subv): Implement.
|
||||
switch (format) {
|
||||
case TextureFormat::DXT1:
|
||||
case TextureFormat::DXT23:
|
||||
case TextureFormat::DXT45:
|
||||
case TextureFormat::A8R8G8B8:
|
||||
case TextureFormat::B5G6R5:
|
||||
// TODO(Subv): For the time being just forward the same data without any decoding.
|
||||
rgba_data = texture_data;
|
||||
break;
|
||||
|
||||
@@ -14,7 +14,8 @@ namespace Texture {
|
||||
/**
|
||||
* Unswizzles a swizzled texture without changing its format.
|
||||
*/
|
||||
std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width, u32 height);
|
||||
std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width, u32 height,
|
||||
u32 block_height = TICEntry::DefaultBlockHeight);
|
||||
|
||||
/**
|
||||
* Decodes an unswizzled texture into a A8R8G8B8 texture.
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
@@ -14,6 +15,7 @@ namespace Texture {
|
||||
|
||||
enum class TextureFormat : u32 {
|
||||
A8R8G8B8 = 0x8,
|
||||
B5G6R5 = 0x15,
|
||||
DXT1 = 0x24,
|
||||
DXT23 = 0x25,
|
||||
DXT45 = 0x26,
|
||||
@@ -57,6 +59,8 @@ union TextureHandle {
|
||||
static_assert(sizeof(TextureHandle) == 4, "TextureHandle has wrong size");
|
||||
|
||||
struct TICEntry {
|
||||
static constexpr u32 DefaultBlockHeight = 16;
|
||||
|
||||
union {
|
||||
u32 raw;
|
||||
BitField<0, 7, TextureFormat> format;
|
||||
@@ -70,7 +74,12 @@ struct TICEntry {
|
||||
BitField<0, 16, u32> address_high;
|
||||
BitField<21, 3, TICHeaderVersion> header_version;
|
||||
};
|
||||
INSERT_PADDING_BYTES(4);
|
||||
union {
|
||||
BitField<3, 3, u32> block_height;
|
||||
|
||||
// High 16 bits of the pitch value
|
||||
BitField<0, 16, u32> pitch_high;
|
||||
};
|
||||
union {
|
||||
BitField<0, 16, u32> width_minus_1;
|
||||
BitField<23, 4, TextureType> texture_type;
|
||||
@@ -82,6 +91,13 @@ struct TICEntry {
|
||||
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | address_low);
|
||||
}
|
||||
|
||||
u32 Pitch() const {
|
||||
ASSERT(header_version == TICHeaderVersion::Pitch ||
|
||||
header_version == TICHeaderVersion::PitchColorKey);
|
||||
// The pitch value is 21 bits, and is 32B aligned.
|
||||
return pitch_high << 5;
|
||||
}
|
||||
|
||||
u32 Width() const {
|
||||
return width_minus_1 + 1;
|
||||
}
|
||||
@@ -90,6 +106,13 @@ struct TICEntry {
|
||||
return height_minus_1 + 1;
|
||||
}
|
||||
|
||||
u32 BlockHeight() const {
|
||||
ASSERT(header_version == TICHeaderVersion::BlockLinear ||
|
||||
header_version == TICHeaderVersion::BlockLinearColorKey);
|
||||
// The block height is stored in log2 format.
|
||||
return 1 << block_height;
|
||||
}
|
||||
|
||||
bool IsTiled() const {
|
||||
return header_version == TICHeaderVersion::BlockLinear ||
|
||||
header_version == TICHeaderVersion::BlockLinearColorKey;
|
||||
|
||||
@@ -44,6 +44,15 @@
|
||||
Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
extern "C" {
|
||||
// tells Nvidia and AMD drivers to use the dedicated GPU by default on laptops with switchable
|
||||
// graphics
|
||||
__declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001;
|
||||
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* "Callouts" are one-time instructional messages shown to the user. In the config settings, there
|
||||
* is a bitfield "callout_flags" options, used to track if a message has already been shown to the
|
||||
|
||||
@@ -37,6 +37,15 @@
|
||||
#include "yuzu_cmd/config.h"
|
||||
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
extern "C" {
|
||||
// tells Nvidia and AMD drivers to use the dedicated GPU by default on laptops with switchable
|
||||
// graphics
|
||||
__declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001;
|
||||
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void PrintHelp(const char* argv0) {
|
||||
std::cout << "Usage: " << argv0
|
||||
<< " [options] <filename>\n"
|
||||
|
||||
Reference in New Issue
Block a user