mirror of
https://github.com/libretro/parallel-gx1.git
synced 2026-11-10 03:11:35 -10:00
The EFB copy to VRAM stages move to the builder: the copy vertex stage, and the copy pixel stage for every format and for the one that reads the format from copy_flags, with the stereo mono-depth read and the depth range the backend has. Each compiles to the same SPIR-V and DXBC words as its GLSL did; TextureConverterShaderGen writes no GLSL now.
562 lines
24 KiB
C++
562 lines
24 KiB
C++
// Copyright 2017 Dolphin Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#include "VideoCommon/TextureConverterShaderGen.h"
|
|
|
|
#include <cstring>
|
|
|
|
#include "VideoCommon/ShaderBuild.h"
|
|
|
|
#include "Common/CommonTypes.h"
|
|
#include "VideoCommon/BPMemory.h"
|
|
#include "VideoCommon/TextureCacheBase.h"
|
|
#include "VideoCommon/VideoCommon.h"
|
|
#include "VideoCommon/VideoConfig.h"
|
|
|
|
namespace TextureConversionShaderGen
|
|
{
|
|
static_assert(COPY_FLAG_ALL_FILTER_COEFS == 1 && COPY_FLAG_EFB_HAS_ALPHA == 2 &&
|
|
COPY_FLAG_FILTER_CAN_OVERFLOW == 4 && COPY_FLAG_APPLY_GAMMA == 8 &&
|
|
COPY_FLAG_DEPTH == 16 && COPY_FLAG_INTENSITY == 32,
|
|
"the pixel shader spells them out");
|
|
|
|
/* A format's channels, in copy_flags: for each of R, G, B and A, the component of the copied
|
|
* colour it takes (2 bits each from bit 8: R, G, B, A), the bits of it the format keeps (3 bits
|
|
* each from bit 16: COPY_MASK_*) and whether it is 1 instead (a bit each from bit 28). */
|
|
enum
|
|
{
|
|
COPY_MASK_FF = 0,
|
|
COPY_MASK_F0,
|
|
COPY_MASK_F8,
|
|
COPY_MASK_FC,
|
|
COPY_MASK_E0
|
|
};
|
|
|
|
static u32 FormatLayout(u32 r, u32 g, u32 b, u32 a, u32 mr, u32 mg, u32 mb, u32 ma, u32 one)
|
|
{
|
|
return (r << 8) | (g << 10) | (b << 12) | (a << 14) | (mr << 16) | (mg << 19) | (mb << 22) |
|
|
(ma << 25) | (one << 28);
|
|
}
|
|
|
|
static u32 GetFormatLayout(EFBCopyFormat dst_format)
|
|
{
|
|
enum
|
|
{
|
|
R = 0,
|
|
G,
|
|
B,
|
|
A
|
|
};
|
|
switch (dst_format)
|
|
{
|
|
case EFBCopyFormat::R4: // R4: float(r & 0xF0) / 240, in all four
|
|
return FormatLayout(R, R, R, R, COPY_MASK_F0, COPY_MASK_F0, COPY_MASK_F0, COPY_MASK_F0, 0);
|
|
case EFBCopyFormat::R8_0x1: // R8
|
|
case EFBCopyFormat::R8: // R8
|
|
return FormatLayout(R, R, R, R, COPY_MASK_FF, COPY_MASK_FF, COPY_MASK_FF, COPY_MASK_FF, 0);
|
|
case EFBCopyFormat::RA4: // RA4: (r, a) & 0xF0 / 240, as rrra
|
|
return FormatLayout(R, R, R, A, COPY_MASK_F0, COPY_MASK_F0, COPY_MASK_F0, COPY_MASK_F0, 0);
|
|
case EFBCopyFormat::RA8:
|
|
return FormatLayout(R, R, R, A, COPY_MASK_FF, COPY_MASK_FF, COPY_MASK_FF, COPY_MASK_FF, 0);
|
|
case EFBCopyFormat::A8:
|
|
return FormatLayout(A, A, A, A, COPY_MASK_FF, COPY_MASK_FF, COPY_MASK_FF, COPY_MASK_FF, 0);
|
|
case EFBCopyFormat::G8:
|
|
return FormatLayout(G, G, G, G, COPY_MASK_FF, COPY_MASK_FF, COPY_MASK_FF, COPY_MASK_FF, 0);
|
|
case EFBCopyFormat::B8:
|
|
return FormatLayout(B, B, B, B, COPY_MASK_FF, COPY_MASK_FF, COPY_MASK_FF, COPY_MASK_FF, 0);
|
|
case EFBCopyFormat::RG8:
|
|
return FormatLayout(R, R, R, G, COPY_MASK_FF, COPY_MASK_FF, COPY_MASK_FF, COPY_MASK_FF, 0);
|
|
case EFBCopyFormat::GB8:
|
|
return FormatLayout(G, G, G, B, COPY_MASK_FF, COPY_MASK_FF, COPY_MASK_FF, COPY_MASK_FF, 0);
|
|
case EFBCopyFormat::RGB565: // r and b & 0xF8 / 248, g & 0xFC / 252, alpha 1
|
|
return FormatLayout(R, G, B, A, COPY_MASK_F8, COPY_MASK_FC, COPY_MASK_F8, COPY_MASK_FF, 8);
|
|
case EFBCopyFormat::RGB5A3:
|
|
// TODO: The MSB controls whether we have RGB5 or RGB4A3, this selection
|
|
// will need to be implemented once we move away from floats.
|
|
return FormatLayout(R, G, B, A, COPY_MASK_F8, COPY_MASK_F8, COPY_MASK_F8, COPY_MASK_E0, 0);
|
|
case EFBCopyFormat::XFB: // rgb / 255, alpha 1
|
|
return FormatLayout(R, G, B, A, COPY_MASK_FF, COPY_MASK_FF, COPY_MASK_FF, COPY_MASK_FF, 8);
|
|
case EFBCopyFormat::RGBA8:
|
|
default:
|
|
return FormatLayout(R, G, B, A, COPY_MASK_FF, COPY_MASK_FF, COPY_MASK_FF, COPY_MASK_FF, 0);
|
|
}
|
|
}
|
|
|
|
EFBCopyFormat GetDrawnFormat(EFBCopyFormat dst_format)
|
|
{
|
|
if (!g_ActiveConfig.bForceTrueColor)
|
|
return dst_format;
|
|
// Increase the precision of EFB copies where it's likely to be safe.
|
|
switch (dst_format)
|
|
{
|
|
case EFBCopyFormat::RGB565:
|
|
// HACK: XFB is RGB8.
|
|
// Don't blindly do this in other places though,
|
|
// the enum value is used to identify XFB copies.
|
|
// The important thing here is that we need alpha = 1.
|
|
return EFBCopyFormat::XFB;
|
|
case EFBCopyFormat::RGB5A3:
|
|
return EFBCopyFormat::RGBA8;
|
|
default:
|
|
// Let's not touch the other formats for now, seems risky.
|
|
return dst_format;
|
|
}
|
|
}
|
|
|
|
u32 GetCopyFlags(EFBCopyFormat dst_format, bool is_depth_copy, bool is_intensity, float gamma_rcp,
|
|
const std::array<u32, 3>& filter_coefficients)
|
|
{
|
|
return (TextureCacheBase::AllCopyFilterCoefsNeeded(filter_coefficients) ?
|
|
COPY_FLAG_ALL_FILTER_COEFS :
|
|
0u) |
|
|
(bpmem.zcontrol.pixel_format == PixelFormat::RGBA6_Z24 ? COPY_FLAG_EFB_HAS_ALPHA : 0u) |
|
|
(TextureCacheBase::CopyFilterCanOverflow(filter_coefficients) ?
|
|
COPY_FLAG_FILTER_CAN_OVERFLOW :
|
|
0u) |
|
|
// If the gamma is needed, then include that too.
|
|
(gamma_rcp != 1.0f ? COPY_FLAG_APPLY_GAMMA : 0u) |
|
|
(is_depth_copy ? COPY_FLAG_DEPTH : 0u) | (is_intensity ? COPY_FLAG_INTENSITY : 0u) |
|
|
GetFormatLayout(GetDrawnFormat(dst_format));
|
|
}
|
|
|
|
/* ---- stages built by calls ---- */
|
|
|
|
namespace
|
|
{
|
|
struct copy_block
|
|
{
|
|
struct rslc_var* m[7]; // src_offset, src_size, filter_coefficients, gamma_rcp, clamp_tb,
|
|
// pixel_height, copy_flags
|
|
};
|
|
|
|
void copy_ps_block(struct shader_build* sb, struct copy_block* blk)
|
|
{
|
|
static const char* const names[7] = {"src_offset", "src_size", "filter_coefficients",
|
|
"gamma_rcp", "clamp_tb", "pixel_height",
|
|
"copy_flags"};
|
|
const struct rslc_type* types[7];
|
|
struct rslc_b_quals q;
|
|
types[0] = sb->f[2];
|
|
types[1] = sb->f[2];
|
|
types[2] = sb->u[3];
|
|
types[3] = sb->f[1];
|
|
types[4] = sb->f[2];
|
|
types[5] = sb->f[1];
|
|
types[6] = sb->u[1];
|
|
rslc_b_quals_init(&q);
|
|
sb_ubo(sb, &q, 1);
|
|
rslc_b_block_q(sb->b, &q, "PSBlock", NULL, types, names, 7, blk->m);
|
|
}
|
|
|
|
/* v_tex0, in or out: in VertexData where there are geometry shaders */
|
|
uint32_t copy_v_tex0(struct shader_build* sb, unsigned storage, struct rslc_var** v)
|
|
{
|
|
static const char* const name = "v_tex0";
|
|
struct rslc_b_quals q;
|
|
rslc_b_quals_init(&q);
|
|
q.storage = storage;
|
|
q.location = sb_varying_location(sb, 0);
|
|
if (g_backend_info.bSupportsGeometryShaders)
|
|
{
|
|
*v = rslc_b_io_block(sb->b, &q, "VertexData", NULL, &sb->f[3], &name, NULL, NULL, 1, -1);
|
|
return 1;
|
|
}
|
|
*v = rslc_b_declare(sb->b, &q, name, sb->f[3], 0);
|
|
return 0;
|
|
}
|
|
|
|
uint32_t copy_tex0(const struct shader_build* sb, struct rslc_var* v, uint32_t in_block)
|
|
{
|
|
return in_block ? sb_mem(sb, sb_ref(sb, v), 0) : sb_ref(sb, v);
|
|
}
|
|
|
|
/* (copy_flags & bit) != 0u */
|
|
uint32_t copy_flag(const struct shader_build* sb, const struct copy_block* blk, uint32_t bit)
|
|
{
|
|
return sb_bin(sb, RSLC_B_NE, sb_bin(sb, RSLC_B_BAND, sb_ref(sb, blk->m[6]), sb_u(sb, bit)),
|
|
sb_u(sb, 0u));
|
|
}
|
|
|
|
uint32_t copy_raw(const struct shader_build* sb, struct rslc_var* raw, const char* c)
|
|
{
|
|
return sb_swz(sb, sb_ref(sb, raw), c);
|
|
}
|
|
|
|
/* float<n>(texcol_raw.c & mask) / div */
|
|
uint32_t copy_masked(const struct shader_build* sb, struct rslc_var* raw, const char* c,
|
|
uint32_t mask, double div)
|
|
{
|
|
const unsigned n = static_cast<unsigned>(strlen(c));
|
|
return sb_bin(sb, RSLC_B_DIV,
|
|
sb_con(sb, sb->f[n], 1, sb_bin(sb, RSLC_B_BAND, copy_raw(sb, raw, c), sb_u(sb, mask))),
|
|
sb_f(sb, div));
|
|
}
|
|
|
|
/* The output of one format, as a shader built for it alone writes it. */
|
|
void copy_format_body(struct shader_build* sb, struct rslc_var* raw, struct rslc_var* ocol0,
|
|
EFBCopyFormat format)
|
|
{
|
|
static const char* const swz[] = {"rrrr", "rrra", "aaaa", "gggg", "bbbb", "rrrg", "gggb"};
|
|
struct rslc_var *a, *b;
|
|
const char* splat = nullptr;
|
|
switch (format)
|
|
{
|
|
case EFBCopyFormat::R4:
|
|
a = rslc_b_local(sb->b, "red", sb->f[1], copy_masked(sb, raw, "r", 0xF0u, 240.0), false);
|
|
sb_set(sb, sb_ref(sb, ocol0),
|
|
sb_con(sb, sb->f[4], 4, sb_ref(sb, a), sb_ref(sb, a), sb_ref(sb, a), sb_ref(sb, a)));
|
|
return;
|
|
case EFBCopyFormat::R8_0x1:
|
|
case EFBCopyFormat::R8:
|
|
splat = swz[0];
|
|
break;
|
|
case EFBCopyFormat::RA4:
|
|
a = rslc_b_local(sb->b, "red_alpha", sb->f[2], copy_masked(sb, raw, "ra", 0xF0u, 240.0),
|
|
false);
|
|
sb_set(sb, sb_ref(sb, ocol0), sb_swz(sb, sb_ref(sb, a), "rrrg"));
|
|
return;
|
|
case EFBCopyFormat::RA8:
|
|
splat = swz[1];
|
|
break;
|
|
case EFBCopyFormat::A8:
|
|
splat = swz[2];
|
|
break;
|
|
case EFBCopyFormat::G8:
|
|
splat = swz[3];
|
|
break;
|
|
case EFBCopyFormat::B8:
|
|
splat = swz[4];
|
|
break;
|
|
case EFBCopyFormat::RG8:
|
|
splat = swz[5];
|
|
break;
|
|
case EFBCopyFormat::GB8:
|
|
splat = swz[6];
|
|
break;
|
|
case EFBCopyFormat::RGB565:
|
|
a = rslc_b_local(sb->b, "red_blue", sb->f[2], copy_masked(sb, raw, "rb", 0xF8u, 248.0),
|
|
false);
|
|
b = rslc_b_local(sb->b, "green", sb->f[1], copy_masked(sb, raw, "g", 0xFCu, 252.0), false);
|
|
sb_set(sb, sb_ref(sb, ocol0),
|
|
sb_con(sb, sb->f[4], 4, sb_swz(sb, sb_ref(sb, a), "r"), sb_ref(sb, b),
|
|
sb_swz(sb, sb_ref(sb, a), "g"), sb_f(sb, 1.0)));
|
|
return;
|
|
case EFBCopyFormat::RGB5A3:
|
|
// TODO: The MSB controls whether we have RGB5 or RGB4A3, this selection
|
|
// will need to be implemented once we move away from floats.
|
|
a = rslc_b_local(sb->b, "color", sb->f[3], copy_masked(sb, raw, "rgb", 0xF8u, 248.0), false);
|
|
b = rslc_b_local(sb->b, "alpha", sb->f[1], copy_masked(sb, raw, "a", 0xE0u, 224.0), false);
|
|
sb_set(sb, sb_ref(sb, ocol0), sb_con(sb, sb->f[4], 2, sb_ref(sb, a), sb_ref(sb, b)));
|
|
return;
|
|
case EFBCopyFormat::XFB:
|
|
sb_set(sb, sb_ref(sb, ocol0),
|
|
sb_con(sb, sb->f[4], 2,
|
|
sb_bin(sb, RSLC_B_DIV, sb_con(sb, sb->f[3], 1, copy_raw(sb, raw, "rgb")),
|
|
sb_f(sb, 255.0)),
|
|
sb_f(sb, 1.0)));
|
|
return;
|
|
case EFBCopyFormat::RGBA8:
|
|
default:
|
|
sb_set(sb, sb_ref(sb, ocol0),
|
|
sb_bin(sb, RSLC_B_DIV, sb_con(sb, sb->f[4], 1, copy_raw(sb, raw, "rgba")),
|
|
sb_f(sb, 255.0)));
|
|
return;
|
|
}
|
|
sb_set(sb, sb_ref(sb, ocol0),
|
|
sb_bin(sb, RSLC_B_DIV, sb_swz(sb, sb_con(sb, sb->f[4], 1, sb_ref(sb, raw)), splat),
|
|
sb_f(sb, 255.0)));
|
|
}
|
|
|
|
/* c == 0u ? r : c == 1u ? g : c == 2u ? b : a */
|
|
uint32_t copy_pick(const struct shader_build* sb, struct rslc_var* source, const char* c,
|
|
struct rslc_var* raw)
|
|
{
|
|
uint32_t e = copy_raw(sb, raw, "a");
|
|
static const char* const ch[3] = {"r", "g", "b"};
|
|
int k;
|
|
for (k = 2; k >= 0; k--)
|
|
e = rslc_b_select(sb->b,
|
|
sb_bin(sb, RSLC_B_EQ, sb_swz(sb, sb_ref(sb, source), c),
|
|
sb_u(sb, static_cast<uint32_t>(k))),
|
|
copy_raw(sb, raw, ch[k]), e);
|
|
return e;
|
|
}
|
|
|
|
/* c == 1u ? 0xF0u : c == 2u ? 0xF8u : c == 3u ? 0xFCu : c == 4u ? 0xE0u : 0xFFu */
|
|
uint32_t copy_mask_of(const struct shader_build* sb, struct rslc_var* mask, const char* c)
|
|
{
|
|
static const uint32_t masks[4] = {0xF0u, 0xF8u, 0xFCu, 0xE0u};
|
|
uint32_t e = sb_u(sb, 0xFFu);
|
|
int k;
|
|
for (k = 3; k >= 0; k--)
|
|
e = rslc_b_select(sb->b,
|
|
sb_bin(sb, RSLC_B_EQ, sb_swz(sb, sb_ref(sb, mask), c),
|
|
sb_u(sb, static_cast<uint32_t>(k + 1))),
|
|
sb_u(sb, masks[k]), e);
|
|
return e;
|
|
}
|
|
|
|
/* (uint4(copy_flags) >> uint4(a, b, c, d)) & m */
|
|
uint32_t copy_fields(const struct shader_build* sb, const struct copy_block* blk, uint32_t a,
|
|
uint32_t b, uint32_t c, uint32_t d, uint32_t m)
|
|
{
|
|
return sb_bin(sb, RSLC_B_BAND,
|
|
sb_bin(sb, RSLC_B_SHR, sb_con(sb, sb->u[4], 1, sb_ref(sb, blk->m[6])),
|
|
sb_con(sb, sb->u[4], 4, sb_u(sb, a), sb_u(sb, b), sb_u(sb, c), sb_u(sb, d))),
|
|
sb_u(sb, m));
|
|
}
|
|
} // Anonymous namespace
|
|
|
|
bool BuildVertexShader(struct shader_build* sb, const void* /* arg */)
|
|
{
|
|
struct copy_block blk;
|
|
struct rslc_var* v;
|
|
struct rslc_func* f;
|
|
uint32_t in_block, x, y;
|
|
copy_ps_block(sb, &blk);
|
|
in_block = copy_v_tex0(sb, RSLC_B_ST_OUT, &v);
|
|
f = rslc_b_func(sb->b, "main", sb->v, 0, NULL, NULL, NULL);
|
|
rslc_b_func_begin(sb->b, f);
|
|
x = sb_con(sb, sb->f[1], 1,
|
|
sb_bin(sb, RSLC_B_BAND,
|
|
sb_bin(sb, RSLC_B_SHL, sb_bi(sb, "gl_VertexIndex"), sb_i(sb, 1)), sb_i(sb, 2)));
|
|
y = sb_con(sb, sb->f[1], 1, sb_bin(sb, RSLC_B_BAND, sb_bi(sb, "gl_VertexIndex"), sb_i(sb, 2)));
|
|
sb_set(sb, copy_tex0(sb, v, in_block), sb_con(sb, sb->f[3], 3, x, y, sb_f(sb, 0.0)));
|
|
sb_set(sb, sb_bi(sb, "gl_Position"),
|
|
sb_con(sb, sb->f[4], 3,
|
|
sb_bin(sb, RSLC_B_ADD,
|
|
sb_bin(sb, RSLC_B_MUL, sb_swz(sb, copy_tex0(sb, v, in_block), "xy"),
|
|
sb_con(sb, sb->f[2], 2, sb_f(sb, 2.0),
|
|
sb_un(sb, RSLC_B_NEG, sb_f(sb, 2.0)))),
|
|
sb_con(sb, sb->f[2], 2, sb_un(sb, RSLC_B_NEG, sb_f(sb, 1.0)),
|
|
sb_f(sb, 1.0))),
|
|
sb_f(sb, 0.0), sb_f(sb, 1.0)));
|
|
sb_set(sb, copy_tex0(sb, v, in_block),
|
|
sb_con(sb, sb->f[3], 2,
|
|
sb_bin(sb, RSLC_B_ADD, sb_ref(sb, blk.m[0]),
|
|
sb_bin(sb, RSLC_B_MUL, sb_ref(sb, blk.m[1]),
|
|
sb_swz(sb, copy_tex0(sb, v, in_block), "xy"))),
|
|
sb_f(sb, 0.0)));
|
|
// NDC space is flipped in Vulkan
|
|
if (g_backend_info.api_type == APIType::Vulkan)
|
|
sb_set(sb, sb_swz(sb, sb_bi(sb, "gl_Position"), "y"),
|
|
sb_un(sb, RSLC_B_NEG, sb_swz(sb, sb_bi(sb, "gl_Position"), "y")));
|
|
return rslc_b_func_end(sb->b);
|
|
}
|
|
|
|
bool BuildPixelShader(struct shader_build* sb, const void* arg)
|
|
{
|
|
static const unsigned in_dirs[2] = {RSLC_B_IN, RSLC_B_IN};
|
|
static const char* const in_names[2] = {"uv", "y_offset"};
|
|
const EFBCopyFormat* const format = static_cast<const EFBCopyFormat*>(arg);
|
|
const struct rslc_type* in_types[2];
|
|
struct copy_block blk;
|
|
struct rslc_b_quals q;
|
|
struct rslc_var *samp, *v, *ocol0, *current, *combined, *raw, *prev, *next;
|
|
struct rslc_func *sample_efb, *f;
|
|
uint32_t in_block, e, z, then_s, else_s;
|
|
|
|
copy_ps_block(sb, &blk);
|
|
rslc_b_quals_init(&q);
|
|
sb_sampler(sb, &q, 0);
|
|
samp = rslc_b_declare(sb->b, &q, "samp0",
|
|
rslc_b_sampler_type(sb->b, RSLC_B_SAMPLER_2D_ARRAY, RSLC_BASE_FLOAT), 0);
|
|
|
|
// A depth copy reads the first layer alone, with stereo's mono depth.
|
|
in_types[0] = sb->f[3];
|
|
in_types[1] = sb->f[1];
|
|
sample_efb = rslc_b_func(sb->b, "SampleEFB", sb->u[4], 2, in_types, in_dirs, in_names);
|
|
rslc_b_func_begin(sb->b, sample_efb);
|
|
{
|
|
struct rslc_var* uv = rslc_b_param(sample_efb, 0);
|
|
struct rslc_var* y_offset = rslc_b_param(sample_efb, 1);
|
|
struct rslc_var *tex_sample, *depth;
|
|
z = sb_swz(sb, sb_ref(sb, uv), "z");
|
|
if (g_ActiveConfig.bStereoEFBMonoDepth)
|
|
z = rslc_b_select(sb->b, copy_flag(sb, &blk, 16u), sb_f(sb, 0.0), z);
|
|
e = sb_call(sb, "clamp", 3,
|
|
sb_bin(sb, RSLC_B_ADD, sb_swz(sb, sb_ref(sb, uv), "y"),
|
|
sb_bin(sb, RSLC_B_MUL, sb_ref(sb, y_offset), sb_ref(sb, blk.m[5]))),
|
|
sb_swz(sb, sb_ref(sb, blk.m[4]), "x"), sb_swz(sb, sb_ref(sb, blk.m[4]), "y"));
|
|
tex_sample = rslc_b_local(
|
|
sb->b, "tex_sample", sb->f[4],
|
|
sb_call(sb, "texture", 2, sb_ref(sb, samp),
|
|
sb_con(sb, sb->f[3], 3, sb_swz(sb, sb_ref(sb, uv), "x"), e, z)),
|
|
false);
|
|
rslc_b_begin_block(sb->b);
|
|
if (!g_backend_info.bSupportsReversedDepthRange)
|
|
sb_set(sb, sb_swz(sb, sb_ref(sb, tex_sample), "x"),
|
|
sb_bin(sb, RSLC_B_SUB, sb_f(sb, 1.0), sb_swz(sb, sb_ref(sb, tex_sample), "x")));
|
|
depth = rslc_b_local(sb->b, "depth", sb->u[1],
|
|
sb_con(sb, sb->u[1], 1,
|
|
sb_bin(sb, RSLC_B_MUL, sb_swz(sb, sb_ref(sb, tex_sample), "x"),
|
|
sb_f(sb, 16777216.0))),
|
|
false);
|
|
rslc_b_stmt(
|
|
sb->b,
|
|
rslc_b_return(
|
|
sb->b,
|
|
sb_con(sb, sb->u[4], 4,
|
|
sb_bin(sb, RSLC_B_BAND,
|
|
sb_bin(sb, RSLC_B_SHR, sb_ref(sb, depth), sb_i(sb, 16)), sb_u(sb, 255u)),
|
|
sb_bin(sb, RSLC_B_BAND,
|
|
sb_bin(sb, RSLC_B_SHR, sb_ref(sb, depth), sb_i(sb, 8)), sb_u(sb, 255u)),
|
|
sb_bin(sb, RSLC_B_BAND, sb_ref(sb, depth), sb_u(sb, 255u)), sb_u(sb, 255u))));
|
|
then_s = rslc_b_end_block(sb->b);
|
|
rslc_b_stmt(sb->b, rslc_b_if(sb->b, copy_flag(sb, &blk, 16u), then_s, 0));
|
|
rslc_b_stmt(sb->b,
|
|
rslc_b_return(sb->b, sb_con(sb, sb->u[4], 1,
|
|
sb_bin(sb, RSLC_B_MUL, sb_ref(sb, tex_sample),
|
|
sb_f(sb, 255.0)))));
|
|
}
|
|
rslc_b_func_end(sb->b);
|
|
|
|
in_block = copy_v_tex0(sb, RSLC_B_ST_IN, &v);
|
|
rslc_b_quals_init(&q);
|
|
q.storage = RSLC_B_ST_OUT;
|
|
q.location = sb_output_location(sb, 0);
|
|
ocol0 = rslc_b_declare(sb->b, &q, "ocol0", sb->f[4], 0);
|
|
|
|
f = rslc_b_func(sb->b, "main", sb->v, 0, NULL, NULL, NULL);
|
|
rslc_b_func_begin(sb->b, f);
|
|
{
|
|
uint32_t a[2];
|
|
// The copy filter applies to both color and depth copies. This has been verified on
|
|
// hardware. The filter is only applied to the RGB channels, the alpha channel is left intact.
|
|
a[0] = copy_tex0(sb, v, in_block);
|
|
a[1] = sb_f(sb, 0.0);
|
|
current = rslc_b_local(sb->b, "current_row", sb->u[4], rslc_b_call(sb->b, sample_efb, a, 2),
|
|
false);
|
|
combined = rslc_b_local(sb->b, "combined_rows", sb->u[3], 0, false);
|
|
rslc_b_begin_block(sb->b);
|
|
a[0] = copy_tex0(sb, v, in_block);
|
|
a[1] = sb_un(sb, RSLC_B_NEG, sb_f(sb, 1.0));
|
|
prev = rslc_b_local(sb->b, "prev_row", sb->u[4], rslc_b_call(sb->b, sample_efb, a, 2), false);
|
|
a[0] = copy_tex0(sb, v, in_block);
|
|
a[1] = sb_f(sb, 1.0);
|
|
next = rslc_b_local(sb->b, "next_row", sb->u[4], rslc_b_call(sb->b, sample_efb, a, 2), false);
|
|
e = sb_bin(sb, RSLC_B_ADD,
|
|
sb_bin(sb, RSLC_B_ADD,
|
|
sb_bin(sb, RSLC_B_MUL, sb_swz(sb, sb_ref(sb, prev), "rgb"),
|
|
sb_idx(sb, sb_ref(sb, blk.m[2]), sb_i(sb, 0))),
|
|
sb_bin(sb, RSLC_B_MUL, sb_swz(sb, sb_ref(sb, current), "rgb"),
|
|
sb_idx(sb, sb_ref(sb, blk.m[2]), sb_i(sb, 1)))),
|
|
sb_bin(sb, RSLC_B_MUL, sb_swz(sb, sb_ref(sb, next), "rgb"),
|
|
sb_idx(sb, sb_ref(sb, blk.m[2]), sb_i(sb, 2))));
|
|
sb_set(sb, sb_ref(sb, combined), e);
|
|
then_s = rslc_b_end_block(sb->b);
|
|
rslc_b_begin_block(sb->b);
|
|
sb_set(sb, sb_ref(sb, combined),
|
|
sb_bin(sb, RSLC_B_MUL, sb_swz(sb, sb_ref(sb, current), "rgb"),
|
|
sb_idx(sb, sb_ref(sb, blk.m[2]), sb_i(sb, 1))));
|
|
else_s = rslc_b_end_block(sb->b);
|
|
rslc_b_stmt(sb->b, rslc_b_if(sb->b, copy_flag(sb, &blk, 1u), then_s, else_s));
|
|
|
|
// Shift right by 6 to divide by 64, as filter coefficients
|
|
// that sum to 64 result in no change in brightness
|
|
raw = rslc_b_local(
|
|
sb->b, "texcol_raw", sb->u[4],
|
|
sb_con(sb, sb->u[4], 2,
|
|
sb_bin(sb, RSLC_B_SHR, sb_swz(sb, sb_ref(sb, combined), "rgb"), sb_i(sb, 6)),
|
|
rslc_b_select(sb->b, copy_flag(sb, &blk, 2u), sb_swz(sb, sb_ref(sb, current), "a"),
|
|
sb_u(sb, 255u))),
|
|
false);
|
|
rslc_b_stmt(sb->b,
|
|
rslc_b_if(sb->b, copy_flag(sb, &blk, 4u),
|
|
rslc_b_expr(sb->b, rslc_b_assign(sb->b, RSLC_B_BAND, sb_ref(sb, raw),
|
|
sb_u(sb, 0x1ffu))),
|
|
0));
|
|
// Note that overflow occurs when the sum of values is >= 128, but this max situation can be
|
|
// hit on >= 64, so we always include it.
|
|
sb_set(sb, sb_ref(sb, raw),
|
|
sb_call(sb, "min", 2, sb_ref(sb, raw),
|
|
sb_con(sb, sb->u[4], 4, sb_i(sb, 255), sb_i(sb, 255), sb_i(sb, 255),
|
|
sb_i(sb, 255))));
|
|
e = sb_call(sb, "pow", 2,
|
|
sb_call(sb, "abs", 1,
|
|
sb_bin(sb, RSLC_B_DIV, sb_con(sb, sb->f[4], 1, sb_ref(sb, raw)),
|
|
sb_f(sb, 255.0))),
|
|
sb_con(sb, sb->f[4], 4, sb_ref(sb, blk.m[3]), sb_ref(sb, blk.m[3]),
|
|
sb_ref(sb, blk.m[3]), sb_f(sb, 1.0)));
|
|
e = sb_con(sb, sb->u[4], 1,
|
|
sb_call(sb, "round", 1, sb_bin(sb, RSLC_B_MUL, e, sb_f(sb, 255.0))));
|
|
rslc_b_stmt(sb->b,
|
|
rslc_b_if(sb->b, copy_flag(sb, &blk, 8u),
|
|
rslc_b_expr(sb->b, rslc_b_assign(sb->b, RSLC_B_SET, sb_ref(sb, raw), e)),
|
|
0));
|
|
|
|
rslc_b_begin_block(sb->b);
|
|
{
|
|
// Intensity/YUV format conversion constants determined by hardware testing
|
|
static const int32_t yuv[3][4] = {{66, 129, 25, 16}, {-38, -74, 112, 128},
|
|
{112, -94, -18, 128}};
|
|
static const char* const yuv_names[3] = {"y_const", "u_const", "v_const"};
|
|
struct rslc_var* cst[3];
|
|
uint32_t d[3];
|
|
int k, j;
|
|
for (k = 0; k < 3; k++)
|
|
{
|
|
uint32_t c[4];
|
|
for (j = 0; j < 4; j++)
|
|
c[j] = yuv[k][j] < 0 ? sb_un(sb, RSLC_B_NEG, sb_i(sb, -yuv[k][j])) : sb_i(sb, yuv[k][j]);
|
|
cst[k] = rslc_b_local(sb->b, yuv_names[k], sb->f[4],
|
|
rslc_b_construct(sb->b, sb->f[4], c, 4), true);
|
|
}
|
|
// Intensity/YUV format conversion
|
|
for (k = 0; k < 3; k++)
|
|
d[k] = sb_call(sb, "dot", 2, sb_ref(sb, cst[k]),
|
|
sb_con(sb, sb->f[4], 2, sb_swz(sb, sb_ref(sb, raw), "rgb"), sb_i(sb, 256)));
|
|
sb_set(sb, sb_swz(sb, sb_ref(sb, raw), "rgb"), rslc_b_construct(sb->b, sb->u[3], d, 3));
|
|
// Divide by 256 and round .5 and higher up
|
|
sb_set(sb, sb_swz(sb, sb_ref(sb, raw), "rgb"),
|
|
sb_bin(sb, RSLC_B_ADD,
|
|
sb_bin(sb, RSLC_B_SHR, sb_swz(sb, sb_ref(sb, raw), "rgb"), sb_i(sb, 8)),
|
|
sb_bin(sb, RSLC_B_BAND,
|
|
sb_bin(sb, RSLC_B_SHR, sb_swz(sb, sb_ref(sb, raw), "rgb"), sb_i(sb, 7)),
|
|
sb_u(sb, 1u))));
|
|
}
|
|
then_s = rslc_b_end_block(sb->b);
|
|
rslc_b_stmt(sb->b, rslc_b_if(sb->b, copy_flag(sb, &blk, 32u), then_s, 0));
|
|
|
|
if (format)
|
|
{
|
|
copy_format_body(sb, raw, ocol0, *format);
|
|
}
|
|
else
|
|
{
|
|
// The format: each channel is a component of texcol_raw, masked to the bits the format
|
|
// keeps and scaled by the mask, which is the largest value they hold; or 1.
|
|
struct rslc_var* source =
|
|
rslc_b_local(sb->b, "source", sb->u[4], copy_fields(sb, &blk, 8u, 10u, 12u, 14u, 3u),
|
|
false);
|
|
struct rslc_var *picked, *mask, *one;
|
|
picked = rslc_b_local(sb->b, "picked", sb->u[4],
|
|
sb_con(sb, sb->u[4], 4, copy_pick(sb, source, "x", raw),
|
|
copy_pick(sb, source, "y", raw), copy_pick(sb, source, "z", raw),
|
|
copy_pick(sb, source, "w", raw)),
|
|
false);
|
|
mask = rslc_b_local(sb->b, "mask", sb->u[4], copy_fields(sb, &blk, 16u, 19u, 22u, 25u, 7u),
|
|
false);
|
|
sb_set(sb, sb_ref(sb, mask),
|
|
sb_con(sb, sb->u[4], 4, copy_mask_of(sb, mask, "x"), copy_mask_of(sb, mask, "y"),
|
|
copy_mask_of(sb, mask, "z"), copy_mask_of(sb, mask, "w")));
|
|
one = rslc_b_local(sb->b, "one", sb->f[4],
|
|
sb_con(sb, sb->f[4], 1, copy_fields(sb, &blk, 28u, 29u, 30u, 31u, 1u)),
|
|
false);
|
|
sb_set(sb, sb_ref(sb, ocol0),
|
|
sb_call(sb, "mix", 3,
|
|
sb_bin(sb, RSLC_B_DIV,
|
|
sb_con(sb, sb->f[4], 1,
|
|
sb_bin(sb, RSLC_B_BAND, sb_ref(sb, picked), sb_ref(sb, mask))),
|
|
sb_con(sb, sb->f[4], 1, sb_ref(sb, mask))),
|
|
sb_con(sb, sb->f[4], 4, sb_f(sb, 1.0), sb_f(sb, 1.0), sb_f(sb, 1.0),
|
|
sb_f(sb, 1.0)),
|
|
sb_ref(sb, one)));
|
|
}
|
|
}
|
|
return rslc_b_func_end(sb->b);
|
|
}
|
|
|
|
} // namespace TextureConversionShaderGen
|
|
|