mirror of
https://github.com/citra-emu/citra.git
synced 2024-11-25 23:40:14 +00:00
Refactored Texture decoding into a new stand-alone api
This commit is contained in:
parent
55c91ae782
commit
1a4c8d510d
@ -1,4 +1,8 @@
|
||||
set(SRCS
|
||||
texture/internal/morton.cpp
|
||||
texture/internal/etc1.cpp
|
||||
texture/codec.cpp
|
||||
texture/internal/codecs.cpp
|
||||
renderer_opengl/gl_rasterizer.cpp
|
||||
renderer_opengl/gl_rasterizer_cache.cpp
|
||||
renderer_opengl/gl_shader_gen.cpp
|
||||
@ -21,6 +25,12 @@ set(SRCS
|
||||
|
||||
set(HEADERS
|
||||
debug_utils/debug_utils.h
|
||||
texture/internal/texture_utils.h
|
||||
texture/internal/morton.h
|
||||
texture/internal/etc1.h
|
||||
texture/codec.h
|
||||
texture/formats.h
|
||||
texture/internal/codecs.h
|
||||
renderer_opengl/gl_rasterizer.h
|
||||
renderer_opengl/gl_rasterizer_cache.h
|
||||
renderer_opengl/gl_resource_manager.h
|
||||
|
143
src/video_core/texture/codec.cpp
Normal file
143
src/video_core/texture/codec.cpp
Normal file
@ -0,0 +1,143 @@
|
||||
#include "codec.h"
|
||||
#include "internal\codecs.h"
|
||||
#include "internal\morton.h"
|
||||
|
||||
namespace Pica {
|
||||
namespace Texture {
|
||||
|
||||
void Codec::decode() {
|
||||
this->init(true);
|
||||
if (this->morton)
|
||||
this->decode_morton_pass();
|
||||
};
|
||||
|
||||
void Codec::encode() {
|
||||
this->init(false);
|
||||
if (this->morton)
|
||||
this->encode_morton_pass();
|
||||
};
|
||||
|
||||
void Codec::setSize() {
|
||||
this->start_nibbles_size = format_size;
|
||||
};
|
||||
|
||||
inline void Codec::setWidth(u32 width) {
|
||||
this->width = width;
|
||||
}
|
||||
|
||||
inline void Codec::setHeight(u32 height) {
|
||||
this->height = height;
|
||||
}
|
||||
|
||||
void Codec::configTiling(bool active, u32 tiling) {
|
||||
this->morton = true;
|
||||
this->morton_pass_tiling = tiling;
|
||||
if (tiling != 8 && tiling != 32) {
|
||||
this->invalid_state = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Codec::configRGBATransform(bool active) {
|
||||
this->raw_RGBA = active;
|
||||
}
|
||||
|
||||
void Codec::configPreConvertedRGBA(bool active) {
|
||||
this->preconverted = active;
|
||||
}
|
||||
|
||||
void Codec::setExternalBuffer(u8* external) {
|
||||
this->external_result_buffer = true;
|
||||
this->passing_buffer = external;
|
||||
}
|
||||
|
||||
std::unique_ptr<u8[]> Codec::transferInternalBuffer() {
|
||||
if (!this->external_result_buffer) {
|
||||
std::unique_ptr<u8[]> result(std::move(this->internal_buffer));
|
||||
return result;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Codec::invalid() {
|
||||
return this->invalid_state;
|
||||
}
|
||||
|
||||
void Codec::init(bool decode) {
|
||||
if (decode) {
|
||||
if (this->raw_RGBA)
|
||||
this->expected_nibbles_size = 8;
|
||||
} else {
|
||||
this->start_nibbles_size = this->format_size;
|
||||
if (this->raw_RGBA)
|
||||
this->expected_nibbles_size = this->format_size;
|
||||
if (this->preconverted)
|
||||
this->start_nibbles_size = 8;
|
||||
}
|
||||
if (!this->external_result_buffer) {
|
||||
size_t buff_size = this->width * this->height * this->expected_nibbles_size / 2;
|
||||
this->internal_buffer = std::make_unique<u8[]>(buff_size);
|
||||
this->passing_buffer = this->internal_buffer.get();
|
||||
}
|
||||
}
|
||||
|
||||
inline void Codec::decode_morton_pass() {
|
||||
if (this->morton_pass_tiling == 8)
|
||||
Decoders::Morton_8x8(this->target_buffer, this->passing_buffer, this->width, this->height,
|
||||
this->start_nibbles_size * 4);
|
||||
else if (this->morton_pass_tiling == 32)
|
||||
Decoders::Morton_32x32(this->target_buffer, this->passing_buffer, this->width, this->height,
|
||||
this->start_nibbles_size * 4);
|
||||
}
|
||||
|
||||
inline void Codec::encode_morton_pass() {
|
||||
if (this->morton_pass_tiling == 8)
|
||||
Encoders::Morton_8x8(this->target_buffer, this->passing_buffer, this->width, this->height,
|
||||
this->start_nibbles_size * 4);
|
||||
else if (this->morton_pass_tiling == 32)
|
||||
Encoders::Morton_32x32(this->target_buffer, this->passing_buffer, this->width, this->height,
|
||||
this->start_nibbles_size * 4);
|
||||
}
|
||||
|
||||
std::unique_ptr<Codec> CodecFactory::build(Format format, u8* target, u32 width, u32 height) {
|
||||
switch (format) {
|
||||
case Format::RGBA8:
|
||||
return std::make_unique<RGBACodec>(target, width, height);
|
||||
case Format::RGB8:
|
||||
return std::make_unique<RGBCodec>(target, width, height);
|
||||
case Format::RGB5A1:
|
||||
return std::make_unique<RGB5A1Codec>(target, width, height);
|
||||
case Format::RGB565:
|
||||
return std::make_unique<RGB565Codec>(target, width, height);
|
||||
case Format::RGBA4:
|
||||
return std::make_unique<RGBA4Codec>(target, width, height);
|
||||
case Format::RG8:
|
||||
return std::make_unique<RG8Codec>(target, width, height);
|
||||
case Format::IA8:
|
||||
return std::make_unique<IA8Codec>(target, width, height);
|
||||
case Format::I8:
|
||||
return std::make_unique<I8Codec>(target, width, height);
|
||||
case Format::A8:
|
||||
return std::make_unique<A8Codec>(target, width, height);
|
||||
case Format::IA4:
|
||||
return std::make_unique<IA4Codec>(target, width, height);
|
||||
case Format::I4:
|
||||
return std::make_unique<I4Codec>(target, width, height);
|
||||
case Format::A4:
|
||||
return std::make_unique<A4Codec>(target, width, height);
|
||||
case Format::ETC1:
|
||||
return std::make_unique<ETC1Codec>(target, width, height);
|
||||
case Format::ETC1A4:
|
||||
return std::make_unique<ETC1A4Codec>(target, width, height);
|
||||
case Format::D16:
|
||||
return std::make_unique<D16Codec>(target, width, height);
|
||||
case Format::D24:
|
||||
return std::make_unique<D24Codec>(target, width, height);
|
||||
case Format::D24S8:
|
||||
return std::make_unique<D24S8Codec>(target, width, height);
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // Texture
|
||||
} // Pica
|
78
src/video_core/texture/codec.h
Normal file
78
src/video_core/texture/codec.h
Normal file
@ -0,0 +1,78 @@
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include "common/common_types.h"
|
||||
#include "formats.h"
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Pica {
|
||||
|
||||
namespace Texture {
|
||||
|
||||
class Codec {
|
||||
|
||||
public:
|
||||
Codec(u8* target, u32 width, u32 height) {
|
||||
this->target_buffer = target;
|
||||
this->setWidth(width);
|
||||
this->setHeight(height);
|
||||
this->setSize();
|
||||
this->expected_nibbles_size = this->start_nibbles_size;
|
||||
}
|
||||
virtual ~Codec() {}
|
||||
|
||||
virtual void decode();
|
||||
virtual void encode();
|
||||
|
||||
void setSize();
|
||||
|
||||
void setWidth(u32 width);
|
||||
void setHeight(u32 height);
|
||||
|
||||
// Common Passes
|
||||
void configTiling(bool active, u32 tiling);
|
||||
void configRGBATransform(bool active);
|
||||
void configPreConvertedRGBA(bool active);
|
||||
|
||||
void setExternalBuffer(u8* external);
|
||||
std::unique_ptr<u8[]> transferInternalBuffer();
|
||||
|
||||
bool invalid();
|
||||
|
||||
protected:
|
||||
u32 width;
|
||||
u32 height;
|
||||
|
||||
// passes
|
||||
bool invalid_state = false;
|
||||
bool morton = true;
|
||||
u32 morton_pass_tiling = 8;
|
||||
bool raw_RGBA = false;
|
||||
bool preconverted = false;
|
||||
bool disable_components = false;
|
||||
u32 disable_components_mask = 0;
|
||||
|
||||
u32 start_nibbles_size;
|
||||
u32 expected_nibbles_size;
|
||||
const u32 format_size = 8;
|
||||
|
||||
u8* target_buffer; // Initial read buffer
|
||||
u8* passing_buffer; // pointer aliasing: Used and modified by passes
|
||||
std::unique_ptr<u8[]> internal_buffer; // used if no external buffer is provided
|
||||
bool external_result_buffer = false;
|
||||
|
||||
void init(bool decode);
|
||||
|
||||
typedef Codec super;
|
||||
|
||||
inline void decode_morton_pass();
|
||||
inline void encode_morton_pass();
|
||||
};
|
||||
|
||||
namespace CodecFactory {
|
||||
std::unique_ptr<Codec> build(Pica::Texture::Format format, u8* target, u32 width, u32 height);
|
||||
};
|
||||
|
||||
} // Texture
|
||||
|
||||
} // Pica
|
37
src/video_core/texture/formats.h
Normal file
37
src/video_core/texture/formats.h
Normal file
@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
namespace Pica {
|
||||
|
||||
namespace Texture {
|
||||
|
||||
enum class Format {
|
||||
// First 5 formats are shared between textures and color buffers
|
||||
RGBA8 = 0,
|
||||
RGB8 = 1,
|
||||
RGB5A1 = 2,
|
||||
RGB565 = 3,
|
||||
RGBA4 = 4,
|
||||
|
||||
// Texture-only formats
|
||||
IA8 = 5,
|
||||
RG8 = 6,
|
||||
I8 = 7,
|
||||
A8 = 8,
|
||||
IA4 = 9,
|
||||
I4 = 10,
|
||||
A4 = 11,
|
||||
ETC1 = 12,
|
||||
ETC1A4 = 13,
|
||||
|
||||
// Depth buffer-only formats
|
||||
D16 = 14,
|
||||
// gap
|
||||
D24 = 16,
|
||||
D24S8 = 17,
|
||||
|
||||
Invalid = 255,
|
||||
};
|
||||
|
||||
} // Texture
|
||||
|
||||
} // Pica
|
10
src/video_core/texture/internal/codecs.cpp
Normal file
10
src/video_core/texture/internal/codecs.cpp
Normal file
@ -0,0 +1,10 @@
|
||||
#include "codecs.h"
|
||||
#include "etc1.h"
|
||||
#include "morton.h"
|
||||
#include "texture_utils.h"
|
||||
|
||||
// Decoders
|
||||
#include "decoders.cpp"
|
||||
|
||||
// Encoders
|
||||
#include "encoders.cpp"
|
177
src/video_core/texture/internal/codecs.h
Normal file
177
src/video_core/texture/internal/codecs.h
Normal file
@ -0,0 +1,177 @@
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/texture/codec.h"
|
||||
|
||||
#pragma once
|
||||
|
||||
// each texture format codec
|
||||
class RGBACodec : public Pica::Texture::Codec {
|
||||
public:
|
||||
RGBACodec(u8* target, u32 width, u32 height) : Pica::Texture::Codec(target, width, height) {}
|
||||
void decode();
|
||||
void encode();
|
||||
|
||||
protected:
|
||||
const u32 format_size = 8;
|
||||
};
|
||||
|
||||
class RGBCodec : public Pica::Texture::Codec {
|
||||
public:
|
||||
RGBCodec(u8* target, u32 width, u32 height) : Pica::Texture::Codec(target, width, height) {}
|
||||
void decode();
|
||||
void encode();
|
||||
|
||||
protected:
|
||||
const u32 format_size = 6;
|
||||
};
|
||||
|
||||
class RGB5A1Codec : public Pica::Texture::Codec {
|
||||
public:
|
||||
RGB5A1Codec(u8* target, u32 width, u32 height) : Pica::Texture::Codec(target, width, height) {}
|
||||
void decode();
|
||||
void encode();
|
||||
|
||||
protected:
|
||||
const u32 format_size = 4;
|
||||
};
|
||||
|
||||
class RGBA4Codec : public Pica::Texture::Codec {
|
||||
public:
|
||||
RGBA4Codec(u8* target, u32 width, u32 height) : Pica::Texture::Codec(target, width, height) {}
|
||||
void decode();
|
||||
void encode();
|
||||
|
||||
protected:
|
||||
const u32 format_size = 4;
|
||||
};
|
||||
|
||||
class RGB565Codec : public Pica::Texture::Codec {
|
||||
public:
|
||||
RGB565Codec(u8* target, u32 width, u32 height) : Pica::Texture::Codec(target, width, height) {}
|
||||
void decode();
|
||||
void encode();
|
||||
|
||||
protected:
|
||||
const u32 format_size = 4;
|
||||
};
|
||||
|
||||
class RG8Codec : public Pica::Texture::Codec {
|
||||
public:
|
||||
RG8Codec(u8* target, u32 width, u32 height) : Pica::Texture::Codec(target, width, height) {}
|
||||
void decode();
|
||||
void encode();
|
||||
|
||||
protected:
|
||||
const u32 format_size = 4;
|
||||
};
|
||||
|
||||
class IA8Codec : public Pica::Texture::Codec {
|
||||
public:
|
||||
IA8Codec(u8* target, u32 width, u32 height) : Pica::Texture::Codec(target, width, height) {}
|
||||
void decode();
|
||||
void encode();
|
||||
|
||||
protected:
|
||||
const u32 format_size = 4;
|
||||
};
|
||||
|
||||
class I8Codec : public Pica::Texture::Codec {
|
||||
public:
|
||||
I8Codec(u8* target, u32 width, u32 height) : Pica::Texture::Codec(target, width, height) {}
|
||||
void decode();
|
||||
void encode();
|
||||
|
||||
protected:
|
||||
const u32 format_size = 2;
|
||||
};
|
||||
|
||||
class A8Codec : public Pica::Texture::Codec {
|
||||
public:
|
||||
A8Codec(u8* target, u32 width, u32 height) : Pica::Texture::Codec(target, width, height) {}
|
||||
void decode();
|
||||
void encode();
|
||||
|
||||
protected:
|
||||
const u32 format_size = 2;
|
||||
};
|
||||
|
||||
class IA4Codec : public Pica::Texture::Codec {
|
||||
public:
|
||||
IA4Codec(u8* target, u32 width, u32 height) : Pica::Texture::Codec(target, width, height) {}
|
||||
void decode();
|
||||
void encode();
|
||||
|
||||
protected:
|
||||
const u32 format_size = 2;
|
||||
};
|
||||
|
||||
class I4Codec : public Pica::Texture::Codec {
|
||||
public:
|
||||
I4Codec(u8* target, u32 width, u32 height) : Pica::Texture::Codec(target, width, height) {}
|
||||
void decode();
|
||||
void encode();
|
||||
|
||||
protected:
|
||||
const u32 format_size = 1;
|
||||
};
|
||||
|
||||
class A4Codec : public Pica::Texture::Codec {
|
||||
public:
|
||||
A4Codec(u8* target, u32 width, u32 height) : Pica::Texture::Codec(target, width, height) {}
|
||||
void decode();
|
||||
void encode();
|
||||
|
||||
protected:
|
||||
const u32 format_size = 1;
|
||||
};
|
||||
|
||||
class ETC1Codec : public Pica::Texture::Codec {
|
||||
public:
|
||||
ETC1Codec(u8* target, u32 width, u32 height) : Pica::Texture::Codec(target, width, height) {}
|
||||
void decode();
|
||||
void encode();
|
||||
|
||||
protected:
|
||||
const u32 format_size = 1;
|
||||
};
|
||||
|
||||
class ETC1A4Codec : public Pica::Texture::Codec {
|
||||
public:
|
||||
ETC1A4Codec(u8* target, u32 width, u32 height) : Pica::Texture::Codec(target, width, height) {}
|
||||
void decode();
|
||||
void encode();
|
||||
|
||||
protected:
|
||||
const u32 format_size = 2;
|
||||
};
|
||||
|
||||
class D16Codec : public Pica::Texture::Codec {
|
||||
public:
|
||||
D16Codec(u8* target, u32 width, u32 height) : Pica::Texture::Codec(target, width, height) {}
|
||||
void decode();
|
||||
void encode();
|
||||
|
||||
protected:
|
||||
const u32 format_size = 4;
|
||||
};
|
||||
|
||||
class D24Codec : public Pica::Texture::Codec {
|
||||
public:
|
||||
D24Codec(u8* target, u32 width, u32 height) : Pica::Texture::Codec(target, width, height) {}
|
||||
void decode();
|
||||
void encode();
|
||||
|
||||
protected:
|
||||
const u32 format_size = 6;
|
||||
};
|
||||
|
||||
class D24S8Codec : public Pica::Texture::Codec {
|
||||
public:
|
||||
D24S8Codec(u8* target, u32 width, u32 height) : Pica::Texture::Codec(target, width, height) {}
|
||||
void decode();
|
||||
void encode();
|
||||
|
||||
protected:
|
||||
const u32 format_size = 8;
|
||||
};
|
261
src/video_core/texture/internal/decoders.cpp
Normal file
261
src/video_core/texture/internal/decoders.cpp
Normal file
@ -0,0 +1,261 @@
|
||||
|
||||
namespace {
|
||||
|
||||
template <const Math::Vec4<u8> decode_func(const u8*)>
|
||||
inline void rgba_pass(u8* read, u8* write) {
|
||||
u32 pixel = decode_func(read).ToRGBA();
|
||||
std::memcpy(write, &pixel, 4);
|
||||
}
|
||||
|
||||
} // Anonymous
|
||||
|
||||
void RGBACodec::decode() {
|
||||
super::decode();
|
||||
if (this->raw_RGBA)
|
||||
image_pass<&rgba_pass<&Color::DecodeRGBA8>, 8, 8, 8>(
|
||||
// clang-format off
|
||||
this->passing_buffer, this->width, this->height
|
||||
// clang-format on
|
||||
);
|
||||
}
|
||||
|
||||
void RGBCodec::decode() {
|
||||
super::decode();
|
||||
if (this->raw_RGBA)
|
||||
image_pass<&rgba_pass<&Color::DecodeRGB8>, 6, 8>(
|
||||
// clang-format off
|
||||
this->passing_buffer, this->width, this->height
|
||||
// clang-format on
|
||||
);
|
||||
}
|
||||
|
||||
void RGB5A1Codec::decode() {
|
||||
super::decode();
|
||||
if (this->raw_RGBA)
|
||||
image_pass<&rgba_pass<&Color::DecodeRGB5A1>, 4, 8>(
|
||||
// clang-format off
|
||||
this->passing_buffer, this->width, this->height
|
||||
// clang-format on
|
||||
);
|
||||
}
|
||||
|
||||
void RGB565Codec::decode() {
|
||||
super::decode();
|
||||
if (this->raw_RGBA)
|
||||
image_pass<&rgba_pass<&Color::DecodeRGB565>, 4, 8>(
|
||||
// clang-format off
|
||||
this->passing_buffer, this->width, this->height
|
||||
// clang-format on
|
||||
);
|
||||
}
|
||||
|
||||
void RGBA4Codec::decode() {
|
||||
super::decode();
|
||||
if (this->raw_RGBA)
|
||||
image_pass<&rgba_pass<&Color::DecodeRGBA4>, 4, 8>(
|
||||
// clang-format off
|
||||
this->passing_buffer, this->width, this->height
|
||||
// clang-format on
|
||||
);
|
||||
}
|
||||
|
||||
void RG8Codec::decode() {
|
||||
super::decode();
|
||||
if (this->raw_RGBA)
|
||||
image_pass<&rgba_pass<&Color::DecodeRG8>, 4, 8>(
|
||||
// clang-format off
|
||||
this->passing_buffer, this->width, this->height
|
||||
// clang-format on
|
||||
);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
inline u16 convert_nibbles(u8 nibbles) {
|
||||
return ((u16)Color::Convert4To8((nibbles & 0xF0) >> 4) << 8) |
|
||||
(u16)Color::Convert4To8((nibbles & 0x0F));
|
||||
}
|
||||
|
||||
inline u32 build_luminance(u8 intensity, u8 alpha) {
|
||||
return (alpha << 24) | (intensity << 16) | (intensity << 8) | intensity;
|
||||
}
|
||||
|
||||
inline void intensity_alpha_pass(u8* read, u8* write) {
|
||||
alignas(4) u8 pixel[2];
|
||||
std::memcpy(pixel, read, 2);
|
||||
u32 result = build_luminance(pixel[0], pixel[1]);
|
||||
std::memcpy(write, &result, 4);
|
||||
}
|
||||
|
||||
inline void intensity_alpha_nibbles_pass(u8* read, u8* write) {
|
||||
alignas(4) u8 pixel[2];
|
||||
std::memcpy(pixel, read, 1);
|
||||
u16 tmp = convert_nibbles(pixel[0]);
|
||||
std::memcpy(pixel, &tmp, 2);
|
||||
u32 result = build_luminance(pixel[0], pixel[1]);
|
||||
std::memcpy(write, &result, 4);
|
||||
}
|
||||
|
||||
inline void intensity_pass(u8* read, u8* write) {
|
||||
alignas(4) u8 pixel[1];
|
||||
std::memcpy(pixel, read, 1);
|
||||
u32 result = build_luminance(pixel[0], 255);
|
||||
std::memcpy(write, &result, 4);
|
||||
}
|
||||
|
||||
inline void intensity_nibbles_pass(u8* read, u8* write) {
|
||||
alignas(4) u8 pixel[2];
|
||||
std::memcpy(pixel, read, 1);
|
||||
u16 tmp = convert_nibbles(pixel[0]);
|
||||
std::memcpy(pixel, &tmp, 2);
|
||||
u32 result = build_luminance(pixel[0], 255);
|
||||
std::memcpy(write, &result, 4);
|
||||
result = build_luminance(pixel[1], 255);
|
||||
std::memcpy(write + 4, &result, 4);
|
||||
}
|
||||
|
||||
inline void alpha_pass(u8* read, u8* write) {
|
||||
alignas(4) u8 pixel[1];
|
||||
std::memcpy(pixel, read, 1);
|
||||
u32 result = build_luminance(0, pixel[0]);
|
||||
std::memcpy(write, &result, 4);
|
||||
}
|
||||
|
||||
inline void alpha_nibbles_pass(u8* read, u8* write) {
|
||||
alignas(4) u8 pixel[2];
|
||||
std::memcpy(pixel, read, 1);
|
||||
u16 tmp = convert_nibbles(pixel[0]);
|
||||
std::memcpy(pixel, &tmp, 2);
|
||||
u32 result = build_luminance(0, pixel[0]);
|
||||
std::memcpy(write, &result, 4);
|
||||
result = build_luminance(0, pixel[1]);
|
||||
std::memcpy(write + 4, &result, 4);
|
||||
}
|
||||
|
||||
} // Anonymous
|
||||
|
||||
void IA8Codec::decode() {
|
||||
super::decode();
|
||||
if (this->raw_RGBA)
|
||||
image_pass<&intensity_alpha_pass, 4, 8>(
|
||||
// clang-format off
|
||||
this->passing_buffer, this->width, this->height
|
||||
// clang-format on
|
||||
);
|
||||
}
|
||||
|
||||
void IA4Codec::decode() {
|
||||
super::decode();
|
||||
if (this->raw_RGBA)
|
||||
image_pass<&intensity_alpha_nibbles_pass, 2, 8>(
|
||||
// clang-format off
|
||||
this->passing_buffer, this->width, this->height
|
||||
// clang-format on
|
||||
);
|
||||
}
|
||||
|
||||
void I8Codec::decode() {
|
||||
super::decode();
|
||||
if (this->raw_RGBA)
|
||||
image_pass<&intensity_pass, 2, 8>(
|
||||
// clang-format off
|
||||
this->passing_buffer, this->width, this->height
|
||||
// clang-format on
|
||||
);
|
||||
}
|
||||
|
||||
void I4Codec::decode() {
|
||||
super::decode();
|
||||
if (this->raw_RGBA)
|
||||
image_pass<&intensity_nibbles_pass, 1, 8>(
|
||||
// clang-format off
|
||||
this->passing_buffer, this->width, this->height
|
||||
// clang-format on
|
||||
);
|
||||
}
|
||||
|
||||
void A8Codec::decode() {
|
||||
super::decode();
|
||||
if (this->raw_RGBA)
|
||||
image_pass<&alpha_pass, 2, 8>(
|
||||
// clang-format off
|
||||
this->passing_buffer, this->width, this->height
|
||||
// clang-format on
|
||||
);
|
||||
}
|
||||
|
||||
void A4Codec::decode() {
|
||||
super::decode();
|
||||
if (this->raw_RGBA)
|
||||
image_pass<&alpha_nibbles_pass, 1, 8>(
|
||||
// clang-format off
|
||||
this->passing_buffer, this->width, this->height
|
||||
// clang-format on
|
||||
);
|
||||
}
|
||||
|
||||
void ETC1Codec::decode() {
|
||||
this->init(true);
|
||||
ETC1(this->target_buffer, this->passing_buffer, this->width, this->height);
|
||||
}
|
||||
|
||||
void ETC1A4Codec::decode() {
|
||||
this->init(true);
|
||||
ETC1A4(this->target_buffer, this->passing_buffer, this->width, this->height);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
inline void expand_depth16_pass(u8* read, u8* write) {
|
||||
alignas(4) u8 pixel[4];
|
||||
std::memcpy(pixel, read, 2);
|
||||
pixel[2] = 255;
|
||||
pixel[3] = 255;
|
||||
std::memcpy(write, pixel, 4);
|
||||
}
|
||||
|
||||
inline void expand_depth24_pass(u8* read, u8* write) {
|
||||
alignas(4) u8 pixel[4];
|
||||
std::memcpy(pixel, read, 3);
|
||||
pixel[3] = 255;
|
||||
std::memcpy(write, pixel, 4);
|
||||
}
|
||||
|
||||
inline void fix_stencil_pass(u8* read, u8* write) {
|
||||
u32 pixel;
|
||||
std::memcpy(&pixel, read, 4);
|
||||
pixel = (pixel << 8) | (pixel >> 24);
|
||||
std::memcpy(write, &pixel, 4);
|
||||
}
|
||||
|
||||
} // Anonymous
|
||||
|
||||
void D16Codec::decode() {
|
||||
super::decode();
|
||||
if (this->raw_RGBA)
|
||||
image_pass<&expand_depth16_pass, 4, 8>(
|
||||
// clang-format off
|
||||
this->passing_buffer, this->width, this->height
|
||||
// clang-format on
|
||||
);
|
||||
}
|
||||
|
||||
void D24Codec::decode() {
|
||||
super::decode();
|
||||
if (this->raw_RGBA)
|
||||
image_pass<&expand_depth24_pass, 6, 8>(
|
||||
// clang-format off
|
||||
this->passing_buffer, this->width, this->height
|
||||
// clang-format on
|
||||
);
|
||||
}
|
||||
|
||||
void D24S8Codec::decode() {
|
||||
super::decode();
|
||||
if (this->raw_RGBA)
|
||||
image_pass<&fix_stencil_pass, 8, 8, 8>(
|
||||
// clang-format off
|
||||
this->passing_buffer, this->width, this->height
|
||||
// clang-format on
|
||||
);
|
||||
}
|
109
src/video_core/texture/internal/encoders.cpp
Normal file
109
src/video_core/texture/internal/encoders.cpp
Normal file
@ -0,0 +1,109 @@
|
||||
|
||||
void RGBACodec::encode() {
|
||||
super::encode();
|
||||
}
|
||||
|
||||
void RGBCodec::encode() {
|
||||
super::encode();
|
||||
}
|
||||
|
||||
void RGB5A1Codec::encode() {
|
||||
super::encode();
|
||||
}
|
||||
|
||||
void RGB565Codec::encode() {
|
||||
super::encode();
|
||||
}
|
||||
|
||||
void RGBA4Codec::encode() {
|
||||
super::encode();
|
||||
}
|
||||
|
||||
void RG8Codec::encode() {
|
||||
super::encode();
|
||||
}
|
||||
|
||||
void IA8Codec::encode() {
|
||||
super::encode();
|
||||
}
|
||||
|
||||
void IA4Codec::encode() {
|
||||
super::encode();
|
||||
}
|
||||
|
||||
void I8Codec::encode() {
|
||||
super::encode();
|
||||
}
|
||||
|
||||
void I4Codec::encode() {
|
||||
super::encode();
|
||||
}
|
||||
|
||||
void A8Codec::encode() {
|
||||
super::encode();
|
||||
}
|
||||
|
||||
void A4Codec::encode() {
|
||||
super::encode();
|
||||
}
|
||||
|
||||
void ETC1Codec::encode() {
|
||||
super::encode();
|
||||
}
|
||||
|
||||
void ETC1A4Codec::encode() {
|
||||
super::encode();
|
||||
}
|
||||
|
||||
namespace Encode {
|
||||
|
||||
inline void contract_depth16_pass(u8* read, u8* write) {
|
||||
alignas(4) u8 pixel[4];
|
||||
std::memcpy(pixel, read, 4);
|
||||
std::memcpy(write, pixel, 2);
|
||||
}
|
||||
|
||||
inline void contract_depth24_pass(u8* read, u8* write) {
|
||||
alignas(4) u8 pixel[4];
|
||||
std::memcpy(pixel, read, 4);
|
||||
std::memcpy(write, pixel, 3);
|
||||
}
|
||||
|
||||
inline void fix_stencil_pass(u8* read, u8* write) {
|
||||
u32 pixel;
|
||||
std::memcpy(&pixel, read, 4);
|
||||
pixel = (pixel >> 24) | (pixel << 8);
|
||||
std::memcpy(write, &pixel, 4);
|
||||
}
|
||||
|
||||
} // Anonymous
|
||||
|
||||
void D16Codec::encode() {
|
||||
super::encode();
|
||||
if (this->raw_RGBA)
|
||||
image_pass<&Encode::contract_depth16_pass, 8, 4, 8>(
|
||||
// clang-format off
|
||||
this->passing_buffer, this->width, this->height
|
||||
// clang-format on
|
||||
);
|
||||
}
|
||||
|
||||
void D24Codec::encode() {
|
||||
super::encode();
|
||||
if (this->raw_RGBA)
|
||||
image_pass<&Encode::contract_depth24_pass, 8, 6>(
|
||||
// clang-format off
|
||||
this->passing_buffer, this->width, this->height
|
||||
// clang-format on
|
||||
);
|
||||
}
|
||||
|
||||
void D24S8Codec::encode() {
|
||||
super::encode();
|
||||
if (this->raw_RGBA)
|
||||
image_pass<&Encode::fix_stencil_pass, 8, 8, 8>(
|
||||
// clang-format off
|
||||
this->passing_buffer, this->width, this->height
|
||||
// clang-format on
|
||||
);
|
||||
}
|
187
src/video_core/texture/internal/etc1.cpp
Normal file
187
src/video_core/texture/internal/etc1.cpp
Normal file
@ -0,0 +1,187 @@
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include "common/assert.h"
|
||||
#include "common/bit_field.h"
|
||||
#include "common/color.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/math_util.h"
|
||||
#include "common/swap.h"
|
||||
#include "common/vector_math.h"
|
||||
#include "etc1.h"
|
||||
#include "texture_utils.h"
|
||||
|
||||
constexpr std::array<u8[2], 8> etc1_modifier_table = {{
|
||||
{2, 8}, {5, 17}, {9, 29}, {13, 42}, {18, 60}, {24, 80}, {33, 106}, {47, 183},
|
||||
}};
|
||||
|
||||
namespace {
|
||||
|
||||
union ETC1Tile {
|
||||
u64 raw;
|
||||
|
||||
// Each of these two is a collection of 16 bits (one per lookup value)
|
||||
BitField<0, 16, u64> table_subindexes;
|
||||
BitField<16, 16, u64> negation_flags;
|
||||
|
||||
unsigned GetTableSubIndex(unsigned index) const {
|
||||
return (table_subindexes >> index) & 1;
|
||||
}
|
||||
|
||||
bool GetNegationFlag(unsigned index) const {
|
||||
return ((negation_flags >> index) & 1) == 1;
|
||||
}
|
||||
|
||||
BitField<32, 1, u64> flip;
|
||||
BitField<33, 1, u64> differential_mode;
|
||||
|
||||
BitField<34, 3, u64> table_index_2;
|
||||
BitField<37, 3, u64> table_index_1;
|
||||
|
||||
union {
|
||||
// delta value + base value
|
||||
BitField<40, 3, s64> db;
|
||||
BitField<43, 5, u64> b;
|
||||
|
||||
BitField<48, 3, s64> dg;
|
||||
BitField<51, 5, u64> g;
|
||||
|
||||
BitField<56, 3, s64> dr;
|
||||
BitField<59, 5, u64> r;
|
||||
} differential;
|
||||
|
||||
union {
|
||||
BitField<40, 4, u64> b2;
|
||||
BitField<44, 4, u64> b1;
|
||||
|
||||
BitField<48, 4, u64> g2;
|
||||
BitField<52, 4, u64> g1;
|
||||
|
||||
BitField<56, 4, u64> r2;
|
||||
BitField<60, 4, u64> r1;
|
||||
} separate;
|
||||
|
||||
const Math::Vec3<u8> GetRGB(u32 x, u32 y) const {
|
||||
int texel = 4 * x + y;
|
||||
|
||||
if (flip)
|
||||
std::swap(x, y);
|
||||
|
||||
// Lookup base value
|
||||
Math::Vec3<int> ret;
|
||||
if (differential_mode) {
|
||||
ret.r() = static_cast<int>(differential.r);
|
||||
ret.g() = static_cast<int>(differential.g);
|
||||
ret.b() = static_cast<int>(differential.b);
|
||||
if (x >= 2) {
|
||||
ret.r() += static_cast<int>(differential.dr);
|
||||
ret.g() += static_cast<int>(differential.dg);
|
||||
ret.b() += static_cast<int>(differential.db);
|
||||
}
|
||||
ret.r() = Color::Convert5To8(ret.r());
|
||||
ret.g() = Color::Convert5To8(ret.g());
|
||||
ret.b() = Color::Convert5To8(ret.b());
|
||||
} else {
|
||||
if (x < 2) {
|
||||
ret.r() = Color::Convert4To8(static_cast<u8>(separate.r1));
|
||||
ret.g() = Color::Convert4To8(static_cast<u8>(separate.g1));
|
||||
ret.b() = Color::Convert4To8(static_cast<u8>(separate.b1));
|
||||
} else {
|
||||
ret.r() = Color::Convert4To8(static_cast<u8>(separate.r2));
|
||||
ret.g() = Color::Convert4To8(static_cast<u8>(separate.g2));
|
||||
ret.b() = Color::Convert4To8(static_cast<u8>(separate.b2));
|
||||
}
|
||||
}
|
||||
|
||||
// Add modifier
|
||||
unsigned table_index =
|
||||
static_cast<int>((x < 2) ? table_index_1.Value() : table_index_2.Value());
|
||||
|
||||
int modifier = etc1_modifier_table[table_index][GetTableSubIndex(texel)];
|
||||
if (GetNegationFlag(texel))
|
||||
modifier *= -1;
|
||||
|
||||
ret.r() = MathUtil::Clamp(ret.r() + modifier, 0, 255);
|
||||
ret.g() = MathUtil::Clamp(ret.g() + modifier, 0, 255);
|
||||
ret.b() = MathUtil::Clamp(ret.b() + modifier, 0, 255);
|
||||
|
||||
return ret.Cast<u8>();
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
inline void etc1_pass(u8* etc1_buffer, u8* linear_buffer, u32 x_blocks) {
|
||||
const size_t line = 8 * 4;
|
||||
alignas(64) u8 tmp[line * 8];
|
||||
for (u32 i = 0; i < 4; i++) {
|
||||
ETC1Tile tile;
|
||||
const size_t index = (i % 2) * (line / 2) + (i / 2) * line * 4;
|
||||
std::memcpy(&tile.raw, &etc1_buffer[i * 8], 8);
|
||||
for (u32 k = 0; k < 4; k++) {
|
||||
for (u32 j = 0; j < 4; j++) {
|
||||
u32 rgba = (tile.GetRGB(j, k).ToRGB()) | 0xFF000000;
|
||||
std::memcpy(&tmp[k * line + j * 4 + index], &rgba, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
tiling_pass<&decode, 8, 8>(linear_buffer, tmp, x_blocks);
|
||||
}
|
||||
|
||||
inline void etc1a4_pass(u8* etc1_buffer, u8* linear_buffer, u32 x_blocks) {
|
||||
const size_t line = 8 * 4;
|
||||
alignas(64) u8 tmp[line * 8];
|
||||
for (u32 i = 0; i < 4; i++) {
|
||||
ETC1Tile tile;
|
||||
u64 alpha_tile;
|
||||
const size_t index = (i % 2) * (line / 2) + (i / 2) * line * 4;
|
||||
std::memcpy(&alpha_tile, &etc1_buffer[i * 16], 8);
|
||||
std::memcpy(&tile.raw, &etc1_buffer[i * 16 + 8], 8);
|
||||
for (u32 k = 0; k < 4; k++) {
|
||||
for (u32 j = 0; j < 4; j++) {
|
||||
u32 alpha = (alpha_tile >> (4 * (j * 4 + k))) & 0x0F;
|
||||
alpha |= (alpha << 4);
|
||||
u32 rgba = tile.GetRGB(j, k).ToRGB() | (alpha << 24);
|
||||
std::memcpy(&tmp[k * line + j * 4 + index], &rgba, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
tiling_pass<&decode, 8, 8>(linear_buffer, tmp, x_blocks);
|
||||
}
|
||||
|
||||
void ETC1A4(u8* etc1_buffer, u8* matrix_buffer, u32 width, u32 height) {
|
||||
const u32 x_blocks = (width / 8);
|
||||
const u32 y_blocks = (height / 8);
|
||||
const size_t line_size = 8 * 4;
|
||||
const size_t tile_size = 8 * 8;
|
||||
const size_t stride_size = width * line_size;
|
||||
matrix_buffer = matrix_buffer + (height * width * 4) - stride_size;
|
||||
for (u32 y = 0; y < y_blocks; y++) {
|
||||
u8* linear_buffer = matrix_buffer;
|
||||
for (u32 x = 0; x != x_blocks; x++) {
|
||||
etc1a4_pass(etc1_buffer, linear_buffer, x_blocks);
|
||||
linear_buffer += line_size;
|
||||
etc1_buffer += tile_size;
|
||||
}
|
||||
matrix_buffer -= stride_size;
|
||||
}
|
||||
}
|
||||
|
||||
void ETC1(u8* etc1_buffer, u8* matrix_buffer, u32 width, u32 height) {
|
||||
const u32 x_blocks = (width / 8);
|
||||
const u32 y_blocks = (height / 8);
|
||||
const size_t line_size = 8 * 4;
|
||||
const size_t tile_size = 8 * 8 / 2;
|
||||
const size_t stride_size = width * line_size;
|
||||
matrix_buffer = matrix_buffer + (height * width * 4) - stride_size;
|
||||
for (u32 y = 0; y < y_blocks; y++) {
|
||||
u8* linear_buffer = matrix_buffer;
|
||||
for (u32 x = 0; x != x_blocks; x++) {
|
||||
etc1_pass(etc1_buffer, linear_buffer, x_blocks);
|
||||
linear_buffer += line_size;
|
||||
etc1_buffer += tile_size;
|
||||
}
|
||||
matrix_buffer -= stride_size;
|
||||
}
|
||||
}
|
7
src/video_core/texture/internal/etc1.h
Normal file
7
src/video_core/texture/internal/etc1.h
Normal file
@ -0,0 +1,7 @@
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
#pragma once
|
||||
|
||||
void ETC1(u8* etc1_buffer, u8* matrix_buffer, u32 width, u32 height);
|
||||
void ETC1A4(u8* etc1_buffer, u8* matrix_buffer, u32 width, u32 height);
|
278
src/video_core/texture/internal/morton.cpp
Normal file
278
src/video_core/texture/internal/morton.cpp
Normal file
@ -0,0 +1,278 @@
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include "common/common_types.h"
|
||||
#include "morton.h"
|
||||
#include "texture_utils.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Optimizations
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
#ifdef _MSC_VER
|
||||
#pragma inline_recursion(on)
|
||||
// Normaly set to 16 by default, the best balance seems to be on 8 for this module
|
||||
#pragma inline_depth(8)
|
||||
// favor fast code over small code.
|
||||
#pragma optimize("t", on)
|
||||
#pragma intrinsic(memcpy)
|
||||
#elif defined(CLANG_OR_GCC)
|
||||
// The next 3 will swizle memory copying to help find the best sse/avx shuffling
|
||||
// in case it's possible. Compilation tests have proven effective use of these
|
||||
// flags on gcc and clang.
|
||||
#pragma GCC optimize("-fpredictive-commoning")
|
||||
#pragma GCC optimize("-ftree-loop-distribute-patterns")
|
||||
#pragma GCC optimize("-ftree-vectorize")
|
||||
// limit inlining
|
||||
#pragma GCC option("--param max-inline-insns-single=128")
|
||||
|
||||
// The beauty of these compiler options is that they generate better code than
|
||||
// hand written intrinsics, since inline expanding memeory transfers can be pattern
|
||||
// matched with vector instructions available in the target.
|
||||
#endif
|
||||
|
||||
#pragma region Z_Order
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Z-Order:
|
||||
//
|
||||
// 0-->1
|
||||
// /
|
||||
// 2-->3
|
||||
//
|
||||
// for more information look at: https://en.wikipedia.org/wiki/Z-order_curve
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#define TOP_LEFT 0
|
||||
#define TOP_RIGHT 1
|
||||
#define BOTTOM_LEFT 2
|
||||
#define BOTTOM_RIGHT 3
|
||||
|
||||
constexpr u32 isRight(u32 block_index) {
|
||||
return (block_index % 2);
|
||||
}
|
||||
|
||||
constexpr u32 isBottom(u32 block_index) {
|
||||
return (block_index / 2);
|
||||
}
|
||||
|
||||
template <void codec(u8*, u8*, size_t), size_t nibbles, u32 blocks, size_t block_size>
|
||||
inline void swizzle_block(u8*& morton_block, u8* linear_block);
|
||||
|
||||
template <void codec(u8*, u8*, size_t), size_t nibbles, u32 block_index, u32 blocks,
|
||||
size_t block_size>
|
||||
inline void swizzle_block_aux(u8*& morton_block, u8* linear_block) {
|
||||
// move the linear_block pointer to the appropiate block
|
||||
const size_t right = isRight(block_index) * (blocks * nibbles) / 2;
|
||||
const size_t down = isBottom(block_index) * block_size;
|
||||
u8* new_linear = linear_block + right + down;
|
||||
swizzle_block<codec, nibbles, blocks, block_size>(morton_block, new_linear);
|
||||
}
|
||||
|
||||
template <void codec(u8*, u8*, size_t), size_t nibbles, u32 blocks, size_t block_size>
|
||||
inline void swizzle_block(u8*& morton_block, u8* linear_block) {
|
||||
const size_t new_block_size = block_size / 2;
|
||||
if (blocks <= 2) {
|
||||
// We handle 2*2 blocks on z-order
|
||||
const size_t read_size = nibbles; // just for clearness. It's the same amount
|
||||
// TOP_LEFT & TOP_RIGHT
|
||||
codec(morton_block, linear_block, read_size);
|
||||
morton_block += read_size;
|
||||
// BOTTOM_LEFT & BOTTOM_RIGHT
|
||||
codec(morton_block, linear_block + new_block_size, read_size);
|
||||
morton_block += read_size;
|
||||
} else {
|
||||
// we divide the block into 4 blocks in z-order corecursively
|
||||
// until we have 2x2 blocks.
|
||||
const u32 subdivide = blocks / 2;
|
||||
swizzle_block_aux<codec, nibbles, TOP_LEFT, subdivide, new_block_size>(morton_block,
|
||||
linear_block);
|
||||
swizzle_block_aux<codec, nibbles, TOP_RIGHT, subdivide, new_block_size>(morton_block,
|
||||
linear_block);
|
||||
swizzle_block_aux<codec, nibbles, BOTTOM_LEFT, subdivide, new_block_size>(morton_block,
|
||||
linear_block);
|
||||
swizzle_block_aux<codec, nibbles, BOTTOM_RIGHT, subdivide, new_block_size>(morton_block,
|
||||
linear_block);
|
||||
}
|
||||
}
|
||||
|
||||
template <void codec(u8*, u8*, size_t), size_t nibbles, size_t lines_per_block>
|
||||
void swizzle_pass(u8* morton_block, u8* linear_block) {
|
||||
const size_t block_size = (lines_per_block * lines_per_block * nibbles) / 2;
|
||||
swizzle_block<codec, nibbles, lines_per_block, block_size>(morton_block, linear_block);
|
||||
}
|
||||
#pragma endregion Z_Order
|
||||
|
||||
template <size_t nibbles, size_t lines_per_block>
|
||||
void encode_pass(u8* morton_buffer, u8* linear_buffer, u32 x_blocks) {
|
||||
const u32 tile_size = (lines_per_block * lines_per_block * nibbles) / 2;
|
||||
alignas(64) u8 tmp[tile_size];
|
||||
tiling_pass<&encode, nibbles, lines_per_block>(linear_buffer, tmp, x_blocks);
|
||||
swizzle_pass<&encode, nibbles, lines_per_block>(morton_buffer, tmp);
|
||||
}
|
||||
|
||||
template <size_t nibbles, size_t lines_per_block>
|
||||
void decode_pass(u8* morton_buffer, u8* linear_buffer, u32 x_blocks) {
|
||||
const u32 tile_size = (lines_per_block * lines_per_block * nibbles) / 2;
|
||||
alignas(64) u8 tmp[tile_size];
|
||||
swizzle_pass<&decode, nibbles, lines_per_block>(morton_buffer, tmp);
|
||||
tiling_pass<&decode, nibbles, lines_per_block>(linear_buffer, tmp, x_blocks);
|
||||
}
|
||||
|
||||
template <void codec(u8*, u8*, u32), size_t nibbles, size_t lines_per_block>
|
||||
void morton_pass(u8* morton_buffer, u8* matrix_buffer, u32 width, u32 height) {
|
||||
const u32 x_blocks = (width / lines_per_block);
|
||||
const u32 y_blocks = (height / lines_per_block);
|
||||
const size_t line_size = (lines_per_block * nibbles) / 2;
|
||||
const size_t tile_size = lines_per_block * line_size;
|
||||
const size_t stride_size = width * line_size;
|
||||
matrix_buffer = matrix_buffer + ((height * width * nibbles) / 2) - stride_size;
|
||||
for (u32 y = 0; y < y_blocks; y++) {
|
||||
u8* linear_buffer = matrix_buffer;
|
||||
for (u32 x = 0; x != x_blocks; x++) {
|
||||
codec(morton_buffer, linear_buffer, x_blocks);
|
||||
linear_buffer += line_size;
|
||||
morton_buffer += tile_size;
|
||||
}
|
||||
matrix_buffer -= stride_size;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Decoders {
|
||||
|
||||
bool Morton_8x8(u8* morton_buffer, u8* matrix_buffer, u32 width, u32 height, u32 bpp) {
|
||||
switch (bpp) {
|
||||
case 4: {
|
||||
morton_pass<&decode_pass<1, 8>, 1, 8>(morton_buffer, matrix_buffer, width, height);
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
morton_pass<&decode_pass<2, 8>, 2, 8>(morton_buffer, matrix_buffer, width, height);
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
case 16: {
|
||||
morton_pass<&decode_pass<4, 8>, 4, 8>(morton_buffer, matrix_buffer, width, height);
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
case 24: {
|
||||
morton_pass<&decode_pass<6, 8>, 6, 8>(morton_buffer, matrix_buffer, width, height);
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
case 32: {
|
||||
morton_pass<&decode_pass<8, 8>, 8, 8>(morton_buffer, matrix_buffer, width, height);
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Morton_32x32(u8* morton_buffer, u8* matrix_buffer, u32 width, u32 height, u32 bpp) {
|
||||
switch (bpp) {
|
||||
case 4: {
|
||||
morton_pass<&decode_pass<1, 32>, 1, 32>(morton_buffer, matrix_buffer, width, height);
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
morton_pass<&decode_pass<2, 32>, 2, 32>(morton_buffer, matrix_buffer, width, height);
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
case 16: {
|
||||
morton_pass<&decode_pass<4, 32>, 4, 32>(morton_buffer, matrix_buffer, width, height);
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
case 24: {
|
||||
morton_pass<&decode_pass<6, 32>, 6, 32>(morton_buffer, matrix_buffer, width, height);
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
case 32: {
|
||||
morton_pass<&decode_pass<8, 32>, 8, 32>(morton_buffer, matrix_buffer, width, height);
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Encoders {
|
||||
|
||||
bool Morton_8x8(u8* morton_buffer, u8* matrix_buffer, u32 width, u32 height, u32 bpp) {
|
||||
switch (bpp) {
|
||||
case 4: {
|
||||
morton_pass<&encode_pass<1, 8>, 1, 8>(morton_buffer, matrix_buffer, width, height);
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
morton_pass<&encode_pass<2, 8>, 2, 8>(morton_buffer, matrix_buffer, width, height);
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
case 16: {
|
||||
morton_pass<&encode_pass<4, 8>, 4, 8>(morton_buffer, matrix_buffer, width, height);
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
case 24: {
|
||||
morton_pass<&encode_pass<6, 8>, 6, 8>(morton_buffer, matrix_buffer, width, height);
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
case 32: {
|
||||
morton_pass<&encode_pass<8, 8>, 8, 8>(morton_buffer, matrix_buffer, width, height);
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Morton_32x32(u8* morton_buffer, u8* matrix_buffer, u32 width, u32 height, u32 bpp) {
|
||||
switch (bpp) {
|
||||
case 4: {
|
||||
morton_pass<&encode_pass<1, 32>, 1, 32>(morton_buffer, matrix_buffer, width, height);
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
morton_pass<&encode_pass<2, 32>, 2, 32>(morton_buffer, matrix_buffer, width, height);
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
case 16: {
|
||||
morton_pass<&encode_pass<4, 32>, 4, 32>(morton_buffer, matrix_buffer, width, height);
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
case 24: {
|
||||
morton_pass<&encode_pass<6, 32>, 6, 32>(morton_buffer, matrix_buffer, width, height);
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
case 32: {
|
||||
morton_pass<&encode_pass<8, 32>, 8, 32>(morton_buffer, matrix_buffer, width, height);
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
15
src/video_core/texture/internal/morton.h
Normal file
15
src/video_core/texture/internal/morton.h
Normal file
@ -0,0 +1,15 @@
|
||||
#include "common/common_types.h"
|
||||
|
||||
#pragma once
|
||||
|
||||
enum class MortonPass { Tile8x8, Tile32x32 };
|
||||
|
||||
namespace Decoders {
|
||||
bool Morton_8x8(u8* morton_buffer, u8* matrix_buffer, u32 width, u32 height, u32 bpp);
|
||||
bool Morton_32x32(u8* morton_buffer, u8* matrix_buffer, u32 width, u32 height, u32 bpp);
|
||||
}
|
||||
|
||||
namespace Encoders {
|
||||
bool Morton_8x8(u8* morton_buffer, u8* matrix_buffer, u32 width, u32 height, u32 bpp);
|
||||
bool Morton_32x32(u8* morton_buffer, u8* matrix_buffer, u32 width, u32 height, u32 bpp);
|
||||
}
|
98
src/video_core/texture/internal/texture_utils.h
Normal file
98
src/video_core/texture/internal/texture_utils.h
Normal file
@ -0,0 +1,98 @@
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include "common/color.h"
|
||||
#include "common/swap.h"
|
||||
|
||||
#pragma once
|
||||
|
||||
#if ((defined(__clang__) || defined(__GNUC__)) && !defined(__INTEL_COMPILER))
|
||||
#define CLANG_OR_GCC
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Optimizations
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
#ifdef _MSC_VER
|
||||
#pragma inline_recursion(on)
|
||||
#elif defined(CLANG_OR_GCC)
|
||||
#pragma GCC optimize("-fpeel-loops")
|
||||
#pragma GCC optimize("-fpredictive-commoning")
|
||||
#pragma GCC optimize("-ftree-loop-distribute-patterns")
|
||||
#pragma GCC optimize("-ftree-vectorize")
|
||||
#endif
|
||||
|
||||
// @param read_size is the amount of bytes each pixel takes
|
||||
inline void decode(u8* morton_pointer, u8* matrix_pointer, size_t read_size) {
|
||||
std::memcpy(matrix_pointer, morton_pointer, read_size);
|
||||
}
|
||||
|
||||
// @param read_size is the amount of bytes each pixel takes
|
||||
inline void encode(u8* morton_pointer, u8* matrix_pointer, size_t read_size) {
|
||||
std::memcpy(morton_pointer, matrix_pointer, read_size);
|
||||
}
|
||||
|
||||
// Pre: width % 8 == 0 && height % 8 == 0
|
||||
template <void pass(u8*, u8*), u32 read_size, u32 write_size, u32 tuning = 2>
|
||||
inline void image_pass_aux_rev(u8* target, u32 width, u32 height) {
|
||||
const u32 nibbles = (read_size < 2) & 0x01;
|
||||
const u32 pixels = width * height / (1 + nibbles);
|
||||
const u32 read_size_amortized = read_size / (2 - nibbles);
|
||||
const u32 write_size_amortized = write_size / (2 - nibbles);
|
||||
const u32 sub_iters = tuning;
|
||||
const u32 iters = pixels / sub_iters;
|
||||
u8* read = target + (pixels - 1) * read_size_amortized;
|
||||
u8* write = target + (pixels - 1) * write_size_amortized;
|
||||
for (u32 i = 0; i < iters; i++) {
|
||||
// Sub_iterations allow the compiler to know a set of inner
|
||||
// iterations within compile time, thus it can do better optimizations.
|
||||
for (u32 k = 0; k < sub_iters; k++) {
|
||||
pass(read, write);
|
||||
read -= read_size_amortized;
|
||||
write -= write_size_amortized;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pre: width % 8 == 0 && height % 8 == 0
|
||||
template <void pass(u8*, u8*), u32 read_size, u32 write_size, u32 tuning = 2>
|
||||
inline void image_pass_aux(u8* target, u32 width, u32 height) {
|
||||
const u32 nibbles = (write_size < 2) & 0x01;
|
||||
const u32 pixels = width * height / (1 + nibbles);
|
||||
const u32 read_size_amortized = read_size / (2 - nibbles);
|
||||
const u32 write_size_amortized = write_size / (2 - nibbles);
|
||||
const u32 sub_iters = tuning;
|
||||
const u32 iters = pixels / sub_iters;
|
||||
u8* read = target;
|
||||
u8* write = target;
|
||||
for (u32 i = 0; i < iters; i++) {
|
||||
// Sub_iterations allow the compiler to know a set of inner
|
||||
// iterations within compile time, thus it can do better optimizations.
|
||||
for (u32 k = 0; k < sub_iters; k++) {
|
||||
pass(read, write);
|
||||
read += read_size_amortized;
|
||||
write += write_size_amortized;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <void pass(u8*, u8*), u32 read_size, u32 write_size, u32 tuning = 2>
|
||||
inline void image_pass(u8* target, u32 width, u32 height) {
|
||||
if (read_size > write_size)
|
||||
image_pass_aux<pass, read_size, write_size, tuning>;
|
||||
else
|
||||
image_pass_aux_rev<pass, read_size, write_size, tuning>;
|
||||
}
|
||||
|
||||
template <void codec(u8*, u8*, size_t), size_t nibbles, size_t lines_per_block>
|
||||
void tiling_pass(u8* linear, u8* tiled, u32 x_blocks) {
|
||||
const size_t tiled_line_size = (lines_per_block * nibbles) / 2;
|
||||
const size_t row_length = x_blocks * tiled_line_size;
|
||||
for (u32 i = 0; i < lines_per_block; i++) {
|
||||
const u32 k = (lines_per_block - 1 - i);
|
||||
const size_t tiled_index = i * tiled_line_size;
|
||||
const size_t linear_index = k * row_length;
|
||||
codec(tiled + tiled_index, linear + linear_index, tiled_line_size);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user