1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <boost/container/small_vector.hpp>
#include "shader_recompiler/frontend/ir/basic_block.h"
#include "shader_recompiler/frontend/ir/ir_emitter.h"
#include "shader_recompiler/frontend/ir/value.h"
#include "shader_recompiler/ir_opt/passes.h"
namespace Shader::Optimization {
namespace {
struct PositionInst {
IR::Inst* inst;
IR::Block* block;
IR::Attribute attr;
};
using PositionInstVector = boost::container::small_vector<PositionInst, 24>;
} // Anonymous namespace
void PositionPass(Environment& env, IR::Program& program) {
if (env.ShaderStage() != Stage::VertexB || env.ReadViewportTransformState()) {
return;
}
Info& info{program.info};
info.uses_render_area = true;
PositionInstVector to_replace;
for (IR::Block* const block : program.post_order_blocks) {
for (IR::Inst& inst : block->Instructions()) {
switch (inst.GetOpcode()) {
case IR::Opcode::SetAttribute: {
const IR::Attribute attr{inst.Arg(0).Attribute()};
switch (attr) {
case IR::Attribute::PositionX:
case IR::Attribute::PositionY: {
to_replace.push_back(PositionInst{.inst = &inst, .block = block, .attr = attr});
break;
}
default:
break;
}
break;
}
default:
break;
}
}
}
for (PositionInst& position_inst : to_replace) {
IR::IREmitter ir{*position_inst.block,
IR::Block::InstructionList::s_iterator_to(*position_inst.inst)};
const IR::F32 value(position_inst.inst->Arg(1));
const IR::F32F64 scale(ir.Imm32(2.f));
const IR::F32 negative_one{ir.Imm32(-1.f)};
switch (position_inst.attr) {
case IR::Attribute::PositionX: {
position_inst.inst->SetArg(
1,
ir.FPFma(value, ir.FPMul(ir.FPRecip(ir.RenderAreaWidth()), scale), negative_one));
break;
}
case IR::Attribute::PositionY: {
position_inst.inst->SetArg(
1,
ir.FPFma(value, ir.FPMul(ir.FPRecip(ir.RenderAreaHeight()), scale), negative_one));
break;
}
default:
break;
}
}
}
// Switch 2 fullscreen blit vertex shaders read a parameter block from constant bank 24 (lowered to
// bank 17, which games never bind) that the console provides itself. For a blit covering the whole
// render target it holds: texel size (0x0/0x4, again at 0x10/0x14), size (0x8/0xc), a UV scale of
// 1 (0x18/0x1c), the UV clamp rectangle 0,0..1,1 (0x30-0x3c, read by the fragment shader), a zero
// dither bias (0x40-0x48) and the integer source rectangle 0,0..W-1,H-1 (0x50-0x5c).
void BlitConstantsPass(Environment& env, IR::Program& program) {
if (env.ShaderStage() != Stage::VertexB && env.ShaderStage() != Stage::Fragment) {
return;
}
bool used{};
for (IR::Block* const block : program.post_order_blocks) {
for (IR::Inst& inst : block->Instructions()) {
const IR::Opcode opcode{inst.GetOpcode()};
if (opcode != IR::Opcode::GetCbufU32 && opcode != IR::Opcode::GetCbufF32 &&
opcode != IR::Opcode::GetCbufU32x2) {
continue;
}
if (!inst.Arg(0).IsImmediate() || inst.Arg(0).U32() != 17 || !inst.Arg(1).IsImmediate()) {
continue;
}
IR::IREmitter ir{*block, IR::Block::InstructionList::s_iterator_to(inst)};
const IR::F32 width{ir.RenderAreaWidth()};
const IR::F32 height{ir.RenderAreaHeight()};
const auto word = [&](u32 offset) -> IR::U32 {
switch (offset) {
case 0x0:
case 0x10:
return ir.BitCast<IR::U32>(IR::F32{ir.FPRecip(width)});
case 0x4:
case 0x14:
return ir.BitCast<IR::U32>(IR::F32{ir.FPRecip(height)});
case 0x8:
return ir.BitCast<IR::U32>(width);
case 0xc:
return ir.BitCast<IR::U32>(height);
case 0x18:
case 0x1c:
case 0x38:
case 0x3c:
return ir.Imm32(0x3f800000U);
case 0x58:
return IR::U32{ir.ConvertFToU(32, ir.FPAdd(width, ir.Imm32(-1.0f)))};
case 0x5c:
return IR::U32{ir.ConvertFToU(32, ir.FPAdd(height, ir.Imm32(-1.0f)))};
default:
return ir.Imm32(0U);
}
};
const u32 offset{inst.Arg(1).U32()};
IR::Value value;
if (opcode == IR::Opcode::GetCbufU32x2) {
value = ir.CompositeConstruct(word(offset), word(offset + 4));
} else if (opcode == IR::Opcode::GetCbufF32) {
value = ir.BitCast<IR::F32>(word(offset));
} else {
value = word(offset);
}
inst.ReplaceUsesWith(value);
used = true;
}
}
if (used) {
program.info.uses_render_area = true;
}
}
} // namespace Shader::Optimization
|