early-access version 3252

This commit is contained in:
pineappleEA 2022-12-25 07:12:57 +01:00
parent eb371309f1
commit 88a4a2d4aa
70 changed files with 2515 additions and 508 deletions

View file

@ -1,7 +1,7 @@
yuzu emulator early access yuzu emulator early access
============= =============
This is the source code for early-access 3251. This is the source code for early-access 3252.
## Legal Notice ## Legal Notice

View file

@ -97,6 +97,7 @@ add_library(common STATIC
point.h point.h
precompiled_headers.h precompiled_headers.h
quaternion.h quaternion.h
range_map.h
reader_writer_queue.h reader_writer_queue.h
ring_buffer.h ring_buffer.h
${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp ${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp

139
src/common/range_map.h Executable file
View file

@ -0,0 +1,139 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <map>
#include <type_traits>
#include "common/common_types.h"
namespace Common {
template <typename KeyTBase, typename ValueT>
class RangeMap {
private:
using KeyT =
std::conditional_t<std::is_signed_v<KeyTBase>, KeyTBase, std::make_signed_t<KeyTBase>>;
public:
explicit RangeMap(ValueT null_value_) : null_value{null_value_} {
container.emplace(std::numeric_limits<KeyT>::min(), null_value);
};
~RangeMap() = default;
void Map(KeyTBase address, KeyTBase address_end, ValueT value) {
KeyT new_address = static_cast<KeyT>(address);
KeyT new_address_end = static_cast<KeyT>(address_end);
if (new_address < 0) {
new_address = 0;
}
if (new_address_end < 0) {
new_address_end = 0;
}
InternalMap(new_address, new_address_end, value);
}
void Unmap(KeyTBase address, KeyTBase address_end) {
Map(address, address_end, null_value);
}
[[nodiscard]] size_t GetContinousSizeFrom(KeyTBase address) const {
const KeyT new_address = static_cast<KeyT>(address);
if (new_address < 0) {
return 0;
}
return ContinousSizeInternal(new_address);
}
[[nodiscard]] ValueT GetValueAt(KeyT address) const {
const KeyT new_address = static_cast<KeyT>(address);
if (new_address < 0) {
return null_value;
}
return GetValueInternal(new_address);
}
private:
using MapType = std::map<KeyT, ValueT>;
using IteratorType = typename MapType::iterator;
using ConstIteratorType = typename MapType::const_iterator;
size_t ContinousSizeInternal(KeyT address) const {
const auto it = GetFirstElemnentBeforeOrOn(address);
if (it == container.end() || it->second == null_value) {
return 0;
}
const auto it_end = std::next(it);
if (it_end == container.end()) {
return std::numeric_limits<KeyT>::max() - address;
}
return it_end->first - address;
}
ValueT GetValueInternal(KeyT address) const {
const auto it = GetFirstElemnentBeforeOrOn(address);
if (it == container.end()) {
return null_value;
}
return it->second;
}
ConstIteratorType GetFirstElemnentBeforeOrOn(KeyT address) const {
auto it = container.lower_bound(address);
if (it == container.begin()) {
return it;
}
if (it != container.end() && (it->first == address)) {
return it;
}
--it;
return it;
}
ValueT GetFirstValueWithin(KeyT address) {
auto it = container.lower_bound(address);
if (it == container.begin()) {
return it->second;
}
if (it == container.end()) [[unlikely]] { // this would be a bug
return null_value;
}
--it;
return it->second;
}
ValueT GetLastValueWithin(KeyT address) {
auto it = container.upper_bound(address);
if (it == container.end()) {
return null_value;
}
if (it == container.begin()) [[unlikely]] { // this would be a bug
return it->second;
}
--it;
return it->second;
}
void InternalMap(KeyT address, KeyT address_end, ValueT value) {
const bool must_add_start = GetFirstValueWithin(address) != value;
const ValueT last_value = GetLastValueWithin(address_end);
const bool must_add_end = last_value != value;
auto it = container.lower_bound(address);
const auto it_end = container.upper_bound(address_end);
while (it != it_end) {
it = container.erase(it);
}
if (must_add_start) {
container.emplace(address, value);
}
if (must_add_end) {
container.emplace(address_end, last_value);
}
}
ValueT null_value;
MapType container;
};
} // namespace Common

View file

@ -219,7 +219,7 @@ std::string EmitGLSL(const Profile& profile, const RuntimeInfo& runtime_info, IR
EmitContext ctx{program, bindings, profile, runtime_info}; EmitContext ctx{program, bindings, profile, runtime_info};
Precolor(program); Precolor(program);
EmitCode(ctx, program); EmitCode(ctx, program);
const std::string version{fmt::format("#version 450{}\n", GlslVersionSpecifier(ctx))}; const std::string version{fmt::format("#version 460{}\n", GlslVersionSpecifier(ctx))};
ctx.header.insert(0, version); ctx.header.insert(0, version);
if (program.shared_memory_size > 0) { if (program.shared_memory_size > 0) {
const auto requested_size{program.shared_memory_size}; const auto requested_size{program.shared_memory_size};

View file

@ -234,6 +234,12 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
case IR::Attribute::FrontFace: case IR::Attribute::FrontFace:
ctx.AddF32("{}=itof(gl_FrontFacing?-1:0);", inst); ctx.AddF32("{}=itof(gl_FrontFacing?-1:0);", inst);
break; break;
case IR::Attribute::BaseInstance:
ctx.AddF32("{}=itof(gl_BaseInstance);", inst);
break;
case IR::Attribute::BaseVertex:
ctx.AddF32("{}=itof(gl_BaseVertex);", inst);
break;
default: default:
throw NotImplementedException("Get attribute {}", attr); throw NotImplementedException("Get attribute {}", attr);
} }
@ -250,6 +256,12 @@ void EmitGetAttributeU32(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, s
case IR::Attribute::VertexId: case IR::Attribute::VertexId:
ctx.AddU32("{}=uint(gl_VertexID);", inst); ctx.AddU32("{}=uint(gl_VertexID);", inst);
break; break;
case IR::Attribute::BaseInstance:
ctx.AddU32("{}=uint(gl_BaseInstance);", inst);
break;
case IR::Attribute::BaseVertex:
ctx.AddU32("{}=uint(gl_BaseVertex);", inst);
break;
default: default:
throw NotImplementedException("Get U32 attribute {}", attr); throw NotImplementedException("Get U32 attribute {}", attr);
} }

View file

@ -339,6 +339,10 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
const Id base{ctx.OpLoad(ctx.U32[1], ctx.base_vertex)}; const Id base{ctx.OpLoad(ctx.U32[1], ctx.base_vertex)};
return ctx.OpBitcast(ctx.F32[1], ctx.OpISub(ctx.U32[1], index, base)); return ctx.OpBitcast(ctx.F32[1], ctx.OpISub(ctx.U32[1], index, base));
} }
case IR::Attribute::BaseInstance:
return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.base_instance));
case IR::Attribute::BaseVertex:
return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.base_vertex));
case IR::Attribute::FrontFace: case IR::Attribute::FrontFace:
return ctx.OpSelect(ctx.F32[1], ctx.OpLoad(ctx.U1, ctx.front_face), return ctx.OpSelect(ctx.F32[1], ctx.OpLoad(ctx.U1, ctx.front_face),
ctx.OpBitcast(ctx.F32[1], ctx.Const(std::numeric_limits<u32>::max())), ctx.OpBitcast(ctx.F32[1], ctx.Const(std::numeric_limits<u32>::max())),
@ -380,6 +384,10 @@ Id EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, Id) {
const Id base{ctx.OpLoad(ctx.U32[1], ctx.base_vertex)}; const Id base{ctx.OpLoad(ctx.U32[1], ctx.base_vertex)};
return ctx.OpISub(ctx.U32[1], index, base); return ctx.OpISub(ctx.U32[1], index, base);
} }
case IR::Attribute::BaseInstance:
return ctx.OpLoad(ctx.U32[1], ctx.base_instance);
case IR::Attribute::BaseVertex:
return ctx.OpLoad(ctx.U32[1], ctx.base_vertex);
default: default:
throw NotImplementedException("Read U32 attribute {}", attr); throw NotImplementedException("Read U32 attribute {}", attr);
} }

View file

@ -1379,18 +1379,28 @@ void EmitContext::DefineInputs(const IR::Program& program) {
if (loads[IR::Attribute::InstanceId]) { if (loads[IR::Attribute::InstanceId]) {
if (profile.support_vertex_instance_id) { if (profile.support_vertex_instance_id) {
instance_id = DefineInput(*this, U32[1], true, spv::BuiltIn::InstanceId); instance_id = DefineInput(*this, U32[1], true, spv::BuiltIn::InstanceId);
if (loads[IR::Attribute::BaseInstance]) {
base_instance = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseVertex);
}
} else { } else {
instance_index = DefineInput(*this, U32[1], true, spv::BuiltIn::InstanceIndex); instance_index = DefineInput(*this, U32[1], true, spv::BuiltIn::InstanceIndex);
base_instance = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseInstance); base_instance = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseInstance);
} }
} else if (loads[IR::Attribute::BaseInstance]) {
base_instance = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseInstance);
} }
if (loads[IR::Attribute::VertexId]) { if (loads[IR::Attribute::VertexId]) {
if (profile.support_vertex_instance_id) { if (profile.support_vertex_instance_id) {
vertex_id = DefineInput(*this, U32[1], true, spv::BuiltIn::VertexId); vertex_id = DefineInput(*this, U32[1], true, spv::BuiltIn::VertexId);
if (loads[IR::Attribute::BaseVertex]) {
base_vertex = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseVertex);
}
} else { } else {
vertex_index = DefineInput(*this, U32[1], true, spv::BuiltIn::VertexIndex); vertex_index = DefineInput(*this, U32[1], true, spv::BuiltIn::VertexIndex);
base_vertex = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseVertex); base_vertex = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseVertex);
} }
} else if (loads[IR::Attribute::BaseVertex]) {
base_vertex = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseVertex);
} }
if (loads[IR::Attribute::FrontFace]) { if (loads[IR::Attribute::FrontFace]) {
front_face = DefineInput(*this, U1, true, spv::BuiltIn::FrontFacing); front_face = DefineInput(*this, U1, true, spv::BuiltIn::FrontFacing);

View file

@ -34,6 +34,11 @@ public:
[[nodiscard]] virtual std::array<u32, 3> WorkgroupSize() const = 0; [[nodiscard]] virtual std::array<u32, 3> WorkgroupSize() const = 0;
[[nodiscard]] virtual bool HasHLEMacroState() const = 0;
[[nodiscard]] virtual std::optional<ReplaceConstant> GetReplaceConstBuffer(u32 bank,
u32 offset) = 0;
virtual void Dump(u64 hash) = 0; virtual void Dump(u64 hash) = 0;
[[nodiscard]] const ProgramHeader& SPH() const noexcept { [[nodiscard]] const ProgramHeader& SPH() const noexcept {

View file

@ -446,6 +446,10 @@ std::string NameOf(Attribute attribute) {
return "ViewportMask"; return "ViewportMask";
case Attribute::FrontFace: case Attribute::FrontFace:
return "FrontFace"; return "FrontFace";
case Attribute::BaseInstance:
return "BaseInstance";
case Attribute::BaseVertex:
return "BaseVertex";
} }
return fmt::format("<reserved attribute {}>", static_cast<int>(attribute)); return fmt::format("<reserved attribute {}>", static_cast<int>(attribute));
} }

View file

@ -219,6 +219,10 @@ enum class Attribute : u64 {
FixedFncTexture9Q = 231, FixedFncTexture9Q = 231,
ViewportMask = 232, ViewportMask = 232,
FrontFace = 255, FrontFace = 255,
// Implementation attributes
BaseInstance = 256,
BaseVertex = 257,
}; };
constexpr size_t NUM_GENERICS = 32; constexpr size_t NUM_GENERICS = 32;

View file

@ -294,6 +294,14 @@ F32 IREmitter::GetAttribute(IR::Attribute attribute, const U32& vertex) {
return Inst<F32>(Opcode::GetAttribute, attribute, vertex); return Inst<F32>(Opcode::GetAttribute, attribute, vertex);
} }
U32 IREmitter::GetAttributeU32(IR::Attribute attribute) {
return GetAttributeU32(attribute, Imm32(0));
}
U32 IREmitter::GetAttributeU32(IR::Attribute attribute, const U32& vertex) {
return Inst<U32>(Opcode::GetAttributeU32, attribute, vertex);
}
void IREmitter::SetAttribute(IR::Attribute attribute, const F32& value, const U32& vertex) { void IREmitter::SetAttribute(IR::Attribute attribute, const F32& value, const U32& vertex) {
Inst(Opcode::SetAttribute, attribute, value, vertex); Inst(Opcode::SetAttribute, attribute, value, vertex);
} }

View file

@ -74,6 +74,8 @@ public:
[[nodiscard]] F32 GetAttribute(IR::Attribute attribute); [[nodiscard]] F32 GetAttribute(IR::Attribute attribute);
[[nodiscard]] F32 GetAttribute(IR::Attribute attribute, const U32& vertex); [[nodiscard]] F32 GetAttribute(IR::Attribute attribute, const U32& vertex);
[[nodiscard]] U32 GetAttributeU32(IR::Attribute attribute);
[[nodiscard]] U32 GetAttributeU32(IR::Attribute attribute, const U32& vertex);
void SetAttribute(IR::Attribute attribute, const F32& value, const U32& vertex); void SetAttribute(IR::Attribute attribute, const F32& value, const U32& vertex);
[[nodiscard]] F32 GetAttributeIndexed(const U32& phys_address); [[nodiscard]] F32 GetAttributeIndexed(const U32& phys_address);

View file

@ -219,7 +219,7 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
} }
Optimization::SsaRewritePass(program); Optimization::SsaRewritePass(program);
Optimization::ConstantPropagationPass(program); Optimization::ConstantPropagationPass(env, program);
Optimization::PositionPass(env, program); Optimization::PositionPass(env, program);

View file

@ -7,6 +7,7 @@
#include <type_traits> #include <type_traits>
#include "common/bit_cast.h" #include "common/bit_cast.h"
#include "shader_recompiler/environment.h"
#include "shader_recompiler/exception.h" #include "shader_recompiler/exception.h"
#include "shader_recompiler/frontend/ir/ir_emitter.h" #include "shader_recompiler/frontend/ir/ir_emitter.h"
#include "shader_recompiler/frontend/ir/value.h" #include "shader_recompiler/frontend/ir/value.h"
@ -515,6 +516,8 @@ void FoldBitCast(IR::Inst& inst, IR::Opcode reverse) {
case IR::Attribute::PrimitiveId: case IR::Attribute::PrimitiveId:
case IR::Attribute::InstanceId: case IR::Attribute::InstanceId:
case IR::Attribute::VertexId: case IR::Attribute::VertexId:
case IR::Attribute::BaseVertex:
case IR::Attribute::BaseInstance:
break; break;
default: default:
return; return;
@ -644,7 +647,37 @@ void FoldFSwizzleAdd(IR::Block& block, IR::Inst& inst) {
} }
} }
void ConstantPropagation(IR::Block& block, IR::Inst& inst) { void FoldConstBuffer(Environment& env, IR::Block& block, IR::Inst& inst) {
const IR::Value bank{inst.Arg(0)};
const IR::Value offset{inst.Arg(1)};
if (!bank.IsImmediate() || !offset.IsImmediate()) {
return;
}
const auto bank_value = bank.U32();
const auto offset_value = offset.U32();
auto replacement = env.GetReplaceConstBuffer(bank_value, offset_value);
if (!replacement) {
return;
}
const auto new_attribute = [replacement]() {
switch (*replacement) {
case ReplaceConstant::BaseInstance:
return IR::Attribute::BaseInstance;
case ReplaceConstant::BaseVertex:
return IR::Attribute::BaseVertex;
default:
throw NotImplementedException("Not implemented replacement variable {}", *replacement);
}
}();
IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
if (inst.GetOpcode() == IR::Opcode::GetCbufU32) {
inst.ReplaceUsesWith(ir.GetAttributeU32(new_attribute));
} else {
inst.ReplaceUsesWith(ir.GetAttribute(new_attribute));
}
}
void ConstantPropagation(Environment& env, IR::Block& block, IR::Inst& inst) {
switch (inst.GetOpcode()) { switch (inst.GetOpcode()) {
case IR::Opcode::GetRegister: case IR::Opcode::GetRegister:
return FoldGetRegister(inst); return FoldGetRegister(inst);
@ -789,18 +822,24 @@ void ConstantPropagation(IR::Block& block, IR::Inst& inst) {
IR::Opcode::CompositeInsertF16x4); IR::Opcode::CompositeInsertF16x4);
case IR::Opcode::FSwizzleAdd: case IR::Opcode::FSwizzleAdd:
return FoldFSwizzleAdd(block, inst); return FoldFSwizzleAdd(block, inst);
case IR::Opcode::GetCbufF32:
case IR::Opcode::GetCbufU32:
if (env.HasHLEMacroState()) {
return FoldConstBuffer(env, block, inst);
}
break;
default: default:
break; break;
} }
} }
} // Anonymous namespace } // Anonymous namespace
void ConstantPropagationPass(IR::Program& program) { void ConstantPropagationPass(Environment& env, IR::Program& program) {
const auto end{program.post_order_blocks.rend()}; const auto end{program.post_order_blocks.rend()};
for (auto it = program.post_order_blocks.rbegin(); it != end; ++it) { for (auto it = program.post_order_blocks.rbegin(); it != end; ++it) {
IR::Block* const block{*it}; IR::Block* const block{*it};
for (IR::Inst& inst : block->Instructions()) { for (IR::Inst& inst : block->Instructions()) {
ConstantPropagation(*block, inst); ConstantPropagation(env, *block, inst);
} }
} }
} }

View file

@ -13,7 +13,7 @@ struct HostTranslateInfo;
namespace Shader::Optimization { namespace Shader::Optimization {
void CollectShaderInfoPass(Environment& env, IR::Program& program); void CollectShaderInfoPass(Environment& env, IR::Program& program);
void ConstantPropagationPass(IR::Program& program); void ConstantPropagationPass(Environment& env, IR::Program& program);
void DeadCodeEliminationPass(IR::Program& program); void DeadCodeEliminationPass(IR::Program& program);
void GlobalMemoryToStorageBufferPass(IR::Program& program); void GlobalMemoryToStorageBufferPass(IR::Program& program);
void IdentityRemovalPass(IR::Program& program); void IdentityRemovalPass(IR::Program& program);

View file

@ -16,6 +16,11 @@
namespace Shader { namespace Shader {
enum class ReplaceConstant : u32 {
BaseInstance,
BaseVertex,
};
enum class TextureType : u32 { enum class TextureType : u32 {
Color1D, Color1D,
ColorArray1D, ColorArray1D,

View file

@ -11,7 +11,7 @@
namespace Shader { namespace Shader {
struct VaryingState { struct VaryingState {
std::bitset<256> mask{}; std::bitset<512> mask{};
void Set(IR::Attribute attribute, bool state = true) { void Set(IR::Attribute attribute, bool state = true) {
mask[static_cast<size_t>(attribute)] = state; mask[static_cast<size_t>(attribute)] = state;

View file

@ -7,6 +7,7 @@ add_executable(tests
common/fibers.cpp common/fibers.cpp
common/host_memory.cpp common/host_memory.cpp
common/param_package.cpp common/param_package.cpp
common/range_map.cpp
common/ring_buffer.cpp common/ring_buffer.cpp
common/scratch_buffer.cpp common/scratch_buffer.cpp
common/unique_function.cpp common/unique_function.cpp

70
src/tests/common/range_map.cpp Executable file
View file

@ -0,0 +1,70 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <stdexcept>
#include <catch2/catch.hpp>
#include "common/range_map.h"
enum class MappedEnum : u32 {
Invalid = 0,
Valid_1 = 1,
Valid_2 = 2,
Valid_3 = 3,
};
TEST_CASE("Range Map: Setup", "[video_core]") {
Common::RangeMap<u64, MappedEnum> my_map(MappedEnum::Invalid);
my_map.Map(3000, 3500, MappedEnum::Valid_1);
my_map.Unmap(3200, 3600);
my_map.Map(4000, 4500, MappedEnum::Valid_2);
my_map.Map(4200, 4400, MappedEnum::Valid_2);
my_map.Map(4200, 4400, MappedEnum::Valid_1);
REQUIRE(my_map.GetContinousSizeFrom(4200) == 200);
REQUIRE(my_map.GetContinousSizeFrom(3000) == 200);
REQUIRE(my_map.GetContinousSizeFrom(2900) == 0);
REQUIRE(my_map.GetValueAt(2900) == MappedEnum::Invalid);
REQUIRE(my_map.GetValueAt(3100) == MappedEnum::Valid_1);
REQUIRE(my_map.GetValueAt(3000) == MappedEnum::Valid_1);
REQUIRE(my_map.GetValueAt(3200) == MappedEnum::Invalid);
REQUIRE(my_map.GetValueAt(4199) == MappedEnum::Valid_2);
REQUIRE(my_map.GetValueAt(4200) == MappedEnum::Valid_1);
REQUIRE(my_map.GetValueAt(4400) == MappedEnum::Valid_2);
REQUIRE(my_map.GetValueAt(4500) == MappedEnum::Invalid);
REQUIRE(my_map.GetValueAt(4600) == MappedEnum::Invalid);
my_map.Unmap(0, 6000);
for (u64 address = 0; address < 10000; address += 1000) {
REQUIRE(my_map.GetContinousSizeFrom(address) == 0);
}
my_map.Map(1000, 3000, MappedEnum::Valid_1);
my_map.Map(4000, 5000, MappedEnum::Valid_1);
my_map.Map(2500, 4100, MappedEnum::Valid_1);
REQUIRE(my_map.GetContinousSizeFrom(1000) == 4000);
my_map.Map(1000, 3000, MappedEnum::Valid_1);
my_map.Map(4000, 5000, MappedEnum::Valid_2);
my_map.Map(2500, 4100, MappedEnum::Valid_3);
REQUIRE(my_map.GetContinousSizeFrom(1000) == 1500);
REQUIRE(my_map.GetContinousSizeFrom(2500) == 1600);
REQUIRE(my_map.GetContinousSizeFrom(4100) == 900);
REQUIRE(my_map.GetValueAt(900) == MappedEnum::Invalid);
REQUIRE(my_map.GetValueAt(1000) == MappedEnum::Valid_1);
REQUIRE(my_map.GetValueAt(2500) == MappedEnum::Valid_3);
REQUIRE(my_map.GetValueAt(4100) == MappedEnum::Valid_2);
REQUIRE(my_map.GetValueAt(5000) == MappedEnum::Invalid);
my_map.Map(2000, 6000, MappedEnum::Valid_3);
REQUIRE(my_map.GetContinousSizeFrom(1000) == 1000);
REQUIRE(my_map.GetContinousSizeFrom(3000) == 3000);
REQUIRE(my_map.GetValueAt(1000) == MappedEnum::Valid_1);
REQUIRE(my_map.GetValueAt(1999) == MappedEnum::Valid_1);
REQUIRE(my_map.GetValueAt(1500) == MappedEnum::Valid_1);
REQUIRE(my_map.GetValueAt(2001) == MappedEnum::Valid_3);
REQUIRE(my_map.GetValueAt(5999) == MappedEnum::Valid_3);
REQUIRE(my_map.GetValueAt(6000) == MappedEnum::Invalid);
}

View file

@ -13,6 +13,7 @@ add_library(video_core STATIC
buffer_cache/buffer_base.h buffer_cache/buffer_base.h
buffer_cache/buffer_cache.cpp buffer_cache/buffer_cache.cpp
buffer_cache/buffer_cache.h buffer_cache/buffer_cache.h
cache_types.h
cdma_pusher.cpp cdma_pusher.cpp
cdma_pusher.h cdma_pusher.h
compatible_formats.cpp compatible_formats.cpp

View file

@ -200,7 +200,16 @@ public:
/// Return true when a CPU region is modified from the CPU /// Return true when a CPU region is modified from the CPU
[[nodiscard]] bool IsRegionCpuModified(VAddr addr, size_t size); [[nodiscard]] bool IsRegionCpuModified(VAddr addr, size_t size);
std::mutex mutex; void SetDrawIndirect(
const Tegra::Engines::DrawManager::IndirectParams* current_draw_indirect_) {
current_draw_indirect = current_draw_indirect_;
}
[[nodiscard]] std::pair<Buffer*, u32> GetDrawIndirectCount();
[[nodiscard]] std::pair<Buffer*, u32> GetDrawIndirectBuffer();
std::recursive_mutex mutex;
Runtime& runtime; Runtime& runtime;
private: private:
@ -272,6 +281,8 @@ private:
void BindHostVertexBuffers(); void BindHostVertexBuffers();
void BindHostDrawIndirectBuffers();
void BindHostGraphicsUniformBuffers(size_t stage); void BindHostGraphicsUniformBuffers(size_t stage);
void BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 binding_index, bool needs_bind); void BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 binding_index, bool needs_bind);
@ -298,6 +309,8 @@ private:
void UpdateVertexBuffer(u32 index); void UpdateVertexBuffer(u32 index);
void UpdateDrawIndirect();
void UpdateUniformBuffers(size_t stage); void UpdateUniformBuffers(size_t stage);
void UpdateStorageBuffers(size_t stage); void UpdateStorageBuffers(size_t stage);
@ -372,6 +385,8 @@ private:
SlotVector<Buffer> slot_buffers; SlotVector<Buffer> slot_buffers;
DelayedDestructionRing<Buffer, 8> delayed_destruction_ring; DelayedDestructionRing<Buffer, 8> delayed_destruction_ring;
const Tegra::Engines::DrawManager::IndirectParams* current_draw_indirect{};
u32 last_index_count = 0; u32 last_index_count = 0;
Binding index_buffer; Binding index_buffer;
@ -380,6 +395,8 @@ private:
std::array<std::array<Binding, NUM_STORAGE_BUFFERS>, NUM_STAGES> storage_buffers; std::array<std::array<Binding, NUM_STORAGE_BUFFERS>, NUM_STAGES> storage_buffers;
std::array<std::array<TextureBufferBinding, NUM_TEXTURE_BUFFERS>, NUM_STAGES> texture_buffers; std::array<std::array<TextureBufferBinding, NUM_TEXTURE_BUFFERS>, NUM_STAGES> texture_buffers;
std::array<Binding, NUM_TRANSFORM_FEEDBACK_BUFFERS> transform_feedback_buffers; std::array<Binding, NUM_TRANSFORM_FEEDBACK_BUFFERS> transform_feedback_buffers;
Binding count_buffer_binding;
Binding indirect_buffer_binding;
std::array<Binding, NUM_COMPUTE_UNIFORM_BUFFERS> compute_uniform_buffers; std::array<Binding, NUM_COMPUTE_UNIFORM_BUFFERS> compute_uniform_buffers;
std::array<Binding, NUM_STORAGE_BUFFERS> compute_storage_buffers; std::array<Binding, NUM_STORAGE_BUFFERS> compute_storage_buffers;
@ -674,6 +691,9 @@ void BufferCache<P>::BindHostGeometryBuffers(bool is_indexed) {
} }
BindHostVertexBuffers(); BindHostVertexBuffers();
BindHostTransformFeedbackBuffers(); BindHostTransformFeedbackBuffers();
if (current_draw_indirect) {
BindHostDrawIndirectBuffers();
}
} }
template <class P> template <class P>
@ -823,6 +843,7 @@ bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept {
template <class P> template <class P>
void BufferCache<P>::CommitAsyncFlushesHigh() { void BufferCache<P>::CommitAsyncFlushesHigh() {
AccumulateFlushes(); AccumulateFlushes();
if (committed_ranges.empty()) { if (committed_ranges.empty()) {
return; return;
} }
@ -869,7 +890,7 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
buffer_id, buffer_id,
}); });
// Align up to avoid cache conflicts // Align up to avoid cache conflicts
constexpr u64 align = 256ULL; constexpr u64 align = 8ULL;
constexpr u64 mask = ~(align - 1ULL); constexpr u64 mask = ~(align - 1ULL);
total_size_bytes += (new_size + align - 1) & mask; total_size_bytes += (new_size + align - 1) & mask;
largest_copy = std::max(largest_copy, new_size); largest_copy = std::max(largest_copy, new_size);
@ -1041,6 +1062,19 @@ void BufferCache<P>::BindHostVertexBuffers() {
} }
} }
template <class P>
void BufferCache<P>::BindHostDrawIndirectBuffers() {
const auto bind_buffer = [this](const Binding& binding) {
Buffer& buffer = slot_buffers[binding.buffer_id];
TouchBuffer(buffer, binding.buffer_id);
SynchronizeBuffer(buffer, binding.cpu_addr, binding.size);
};
if (current_draw_indirect->include_count) {
bind_buffer(count_buffer_binding);
}
bind_buffer(indirect_buffer_binding);
}
template <class P> template <class P>
void BufferCache<P>::BindHostGraphicsUniformBuffers(size_t stage) { void BufferCache<P>::BindHostGraphicsUniformBuffers(size_t stage) {
u32 dirty = ~0U; u32 dirty = ~0U;
@ -1272,6 +1306,9 @@ void BufferCache<P>::DoUpdateGraphicsBuffers(bool is_indexed) {
UpdateStorageBuffers(stage); UpdateStorageBuffers(stage);
UpdateTextureBuffers(stage); UpdateTextureBuffers(stage);
} }
if (current_draw_indirect) {
UpdateDrawIndirect();
}
} while (has_deleted_buffers); } while (has_deleted_buffers);
} }
@ -1289,7 +1326,7 @@ void BufferCache<P>::UpdateIndexBuffer() {
const auto& draw_state = maxwell3d->draw_manager->GetDrawState(); const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
const auto& index_array = draw_state.index_buffer; const auto& index_array = draw_state.index_buffer;
auto& flags = maxwell3d->dirty.flags; auto& flags = maxwell3d->dirty.flags;
if (!flags[Dirty::IndexBuffer] && last_index_count == index_array.count) { if (!flags[Dirty::IndexBuffer]) {
return; return;
} }
flags[Dirty::IndexBuffer] = false; flags[Dirty::IndexBuffer] = false;
@ -1361,6 +1398,27 @@ void BufferCache<P>::UpdateVertexBuffer(u32 index) {
}; };
} }
template <class P>
void BufferCache<P>::UpdateDrawIndirect() {
const auto update = [this](GPUVAddr gpu_addr, size_t size, Binding& binding) {
const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
if (!cpu_addr) {
binding = NULL_BINDING;
return;
}
binding = Binding{
.cpu_addr = *cpu_addr,
.size = static_cast<u32>(size),
.buffer_id = FindBuffer(*cpu_addr, static_cast<u32>(size)),
};
};
if (current_draw_indirect->include_count) {
update(current_draw_indirect->count_start_address, sizeof(u32), count_buffer_binding);
}
update(current_draw_indirect->indirect_start_address, current_draw_indirect->buffer_size,
indirect_buffer_binding);
}
template <class P> template <class P>
void BufferCache<P>::UpdateUniformBuffers(size_t stage) { void BufferCache<P>::UpdateUniformBuffers(size_t stage) {
ForEachEnabledBit(enabled_uniform_buffer_masks[stage], [&](u32 index) { ForEachEnabledBit(enabled_uniform_buffer_masks[stage], [&](u32 index) {
@ -1941,4 +1999,16 @@ bool BufferCache<P>::HasFastUniformBufferBound(size_t stage, u32 binding_index)
} }
} }
template <class P>
std::pair<typename BufferCache<P>::Buffer*, u32> BufferCache<P>::GetDrawIndirectCount() {
auto& buffer = slot_buffers[count_buffer_binding.buffer_id];
return std::make_pair(&buffer, buffer.Offset(count_buffer_binding.cpu_addr));
}
template <class P>
std::pair<typename BufferCache<P>::Buffer*, u32> BufferCache<P>::GetDrawIndirectBuffer() {
auto& buffer = slot_buffers[indirect_buffer_binding.buffer_id];
return std::make_pair(&buffer, buffer.Offset(indirect_buffer_binding.cpu_addr));
}
} // namespace VideoCommon } // namespace VideoCommon

24
src/video_core/cache_types.h Executable file
View file

@ -0,0 +1,24 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_funcs.h"
#include "common/common_types.h"
namespace VideoCommon {
enum class CacheType : u32 {
None = 0,
TextureCache = 1 << 0,
QueryCache = 1 << 1,
BufferCache = 1 << 2,
ShaderCache = 1 << 3,
NoTextureCache = QueryCache | BufferCache | ShaderCache,
NoBufferCache = TextureCache | QueryCache | ShaderCache,
NoQueryCache = TextureCache | BufferCache | ShaderCache,
All = TextureCache | QueryCache | BufferCache | ShaderCache,
};
DECLARE_ENUM_FLAG_OPERATORS(CacheType)
} // namespace VideoCommon

View file

@ -61,7 +61,7 @@ bool DmaPusher::Step() {
} else { } else {
const CommandListHeader command_list_header{ const CommandListHeader command_list_header{
command_list.command_lists[dma_pushbuffer_subindex++]}; command_list.command_lists[dma_pushbuffer_subindex++]};
const GPUVAddr dma_get = command_list_header.addr; dma_state.dma_get = command_list_header.addr;
if (dma_pushbuffer_subindex >= command_list.command_lists.size()) { if (dma_pushbuffer_subindex >= command_list.command_lists.size()) {
// We've gone through the current list, remove it from the queue // We've gone through the current list, remove it from the queue
@ -75,12 +75,22 @@ bool DmaPusher::Step() {
// Push buffer non-empty, read a word // Push buffer non-empty, read a word
command_headers.resize_destructive(command_list_header.size); command_headers.resize_destructive(command_list_header.size);
if (Settings::IsGPULevelHigh()) { constexpr u32 MacroRegistersStart = 0xE00;
memory_manager.ReadBlock(dma_get, command_headers.data(), if (dma_state.method < MacroRegistersStart) {
command_list_header.size * sizeof(u32)); if (Settings::IsGPULevelHigh()) {
memory_manager.ReadBlock(dma_state.dma_get, command_headers.data(),
command_list_header.size * sizeof(u32));
} else {
memory_manager.ReadBlockUnsafe(dma_state.dma_get, command_headers.data(),
command_list_header.size * sizeof(u32));
}
} else { } else {
memory_manager.ReadBlockUnsafe(dma_get, command_headers.data(), const size_t copy_size = command_list_header.size * sizeof(u32);
command_list_header.size * sizeof(u32)); if (subchannels[dma_state.subchannel]) {
subchannels[dma_state.subchannel]->current_dirty =
memory_manager.IsMemoryDirty(dma_state.dma_get, copy_size);
}
memory_manager.ReadBlockUnsafe(dma_state.dma_get, command_headers.data(), copy_size);
} }
ProcessCommands(command_headers); ProcessCommands(command_headers);
} }
@ -94,6 +104,7 @@ void DmaPusher::ProcessCommands(std::span<const CommandHeader> commands) {
if (dma_state.method_count) { if (dma_state.method_count) {
// Data word of methods command // Data word of methods command
dma_state.dma_word_offset = static_cast<u32>(index * sizeof(u32));
if (dma_state.non_incrementing) { if (dma_state.non_incrementing) {
const u32 max_write = static_cast<u32>( const u32 max_write = static_cast<u32>(
std::min<std::size_t>(index + dma_state.method_count, commands.size()) - index); std::min<std::size_t>(index + dma_state.method_count, commands.size()) - index);
@ -132,6 +143,8 @@ void DmaPusher::ProcessCommands(std::span<const CommandHeader> commands) {
case SubmissionMode::Inline: case SubmissionMode::Inline:
dma_state.method = command_header.method; dma_state.method = command_header.method;
dma_state.subchannel = command_header.subchannel; dma_state.subchannel = command_header.subchannel;
dma_state.dma_word_offset = static_cast<u64>(
-static_cast<s64>(dma_state.dma_get)); // negate to set address as 0
CallMethod(command_header.arg_count); CallMethod(command_header.arg_count);
dma_state.non_incrementing = true; dma_state.non_incrementing = true;
dma_increment_once = false; dma_increment_once = false;
@ -164,8 +177,14 @@ void DmaPusher::CallMethod(u32 argument) const {
dma_state.method_count, dma_state.method_count,
}); });
} else { } else {
subchannels[dma_state.subchannel]->CallMethod(dma_state.method, argument, auto subchannel = subchannels[dma_state.subchannel];
dma_state.is_last_call); if (!subchannel->execution_mask[dma_state.method]) [[likely]] {
subchannel->method_sink.emplace_back(dma_state.method, argument);
return;
}
subchannel->ConsumeSink();
subchannel->current_dma_segment = dma_state.dma_get + dma_state.dma_word_offset;
subchannel->CallMethod(dma_state.method, argument, dma_state.is_last_call);
} }
} }
@ -174,8 +193,11 @@ void DmaPusher::CallMultiMethod(const u32* base_start, u32 num_methods) const {
puller.CallMultiMethod(dma_state.method, dma_state.subchannel, base_start, num_methods, puller.CallMultiMethod(dma_state.method, dma_state.subchannel, base_start, num_methods,
dma_state.method_count); dma_state.method_count);
} else { } else {
subchannels[dma_state.subchannel]->CallMultiMethod(dma_state.method, base_start, auto subchannel = subchannels[dma_state.subchannel];
num_methods, dma_state.method_count); subchannel->ConsumeSink();
subchannel->current_dma_segment = dma_state.dma_get + dma_state.dma_word_offset;
subchannel->CallMultiMethod(dma_state.method, base_start, num_methods,
dma_state.method_count);
} }
} }

View file

@ -156,6 +156,8 @@ private:
u32 subchannel; ///< Current subchannel u32 subchannel; ///< Current subchannel
u32 method_count; ///< Current method count u32 method_count; ///< Current method count
u32 length_pending; ///< Large NI command length pending u32 length_pending; ///< Large NI command length pending
GPUVAddr dma_get; ///< Currently read segment
u64 dma_word_offset; ///< Current word ofset from address
bool non_incrementing; ///< Current command's NI flag bool non_incrementing; ///< Current command's NI flag
bool is_last_call; bool is_last_call;
}; };

View file

@ -91,6 +91,23 @@ void DrawManager::DrawIndex(PrimitiveTopology topology, u32 index_first, u32 ind
ProcessDraw(true, num_instances); ProcessDraw(true, num_instances);
} }
void DrawManager::DrawArrayIndirect(PrimitiveTopology topology) {
draw_state.topology = topology;
ProcessDrawIndirect(true);
}
void DrawManager::DrawIndexedIndirect(PrimitiveTopology topology, u32 index_first,
u32 index_count) {
const auto& regs{maxwell3d->regs};
draw_state.topology = topology;
draw_state.index_buffer = regs.index_buffer;
draw_state.index_buffer.first = index_first;
draw_state.index_buffer.count = index_count;
ProcessDrawIndirect(true);
}
void DrawManager::SetInlineIndexBuffer(u32 index) { void DrawManager::SetInlineIndexBuffer(u32 index) {
draw_state.inline_index_draw_indexes.push_back(static_cast<u8>(index & 0x000000ff)); draw_state.inline_index_draw_indexes.push_back(static_cast<u8>(index & 0x000000ff));
draw_state.inline_index_draw_indexes.push_back(static_cast<u8>((index & 0x0000ff00) >> 8)); draw_state.inline_index_draw_indexes.push_back(static_cast<u8>((index & 0x0000ff00) >> 8));
@ -198,4 +215,15 @@ void DrawManager::ProcessDraw(bool draw_indexed, u32 instance_count) {
maxwell3d->rasterizer->Draw(draw_indexed, instance_count); maxwell3d->rasterizer->Draw(draw_indexed, instance_count);
} }
} }
void DrawManager::ProcessDrawIndirect(bool draw_indexed) {
LOG_TRACE(HW_GPU, "called, topology={}, count={}", draw_state.topology,
draw_indexed ? draw_state.index_buffer.count : draw_state.vertex_buffer.count);
UpdateTopology();
if (maxwell3d->ShouldExecute()) {
maxwell3d->rasterizer->DrawIndirect();
}
}
} // namespace Tegra::Engines } // namespace Tegra::Engines

View file

@ -32,6 +32,16 @@ public:
std::vector<u8> inline_index_draw_indexes; std::vector<u8> inline_index_draw_indexes;
}; };
struct IndirectParams {
bool is_indexed;
bool include_count;
GPUVAddr count_start_address;
GPUVAddr indirect_start_address;
size_t buffer_size;
size_t max_draw_counts;
size_t stride;
};
explicit DrawManager(Maxwell3D* maxwell_3d); explicit DrawManager(Maxwell3D* maxwell_3d);
void ProcessMethodCall(u32 method, u32 argument); void ProcessMethodCall(u32 method, u32 argument);
@ -46,10 +56,22 @@ public:
void DrawIndex(PrimitiveTopology topology, u32 index_first, u32 index_count, u32 base_index, void DrawIndex(PrimitiveTopology topology, u32 index_first, u32 index_count, u32 base_index,
u32 base_instance, u32 num_instances); u32 base_instance, u32 num_instances);
void DrawArrayIndirect(PrimitiveTopology topology);
void DrawIndexedIndirect(PrimitiveTopology topology, u32 index_first, u32 index_count);
const State& GetDrawState() const { const State& GetDrawState() const {
return draw_state; return draw_state;
} }
IndirectParams& GetIndirectParams() {
return indirect_state;
}
const IndirectParams& GetIndirectParams() const {
return indirect_state;
}
private: private:
void SetInlineIndexBuffer(u32 index); void SetInlineIndexBuffer(u32 index);
@ -63,7 +85,10 @@ private:
void ProcessDraw(bool draw_indexed, u32 instance_count); void ProcessDraw(bool draw_indexed, u32 instance_count);
void ProcessDrawIndirect(bool draw_indexed);
Maxwell3D* maxwell3d{}; Maxwell3D* maxwell3d{};
State draw_state{}; State draw_state{};
IndirectParams indirect_state{};
}; };
} // namespace Tegra::Engines } // namespace Tegra::Engines

View file

@ -3,6 +3,10 @@
#pragma once #pragma once
#include <bitset>
#include <limits>
#include <vector>
#include "common/common_types.h" #include "common/common_types.h"
namespace Tegra::Engines { namespace Tegra::Engines {
@ -17,6 +21,26 @@ public:
/// Write multiple values to the register identified by method. /// Write multiple values to the register identified by method.
virtual void CallMultiMethod(u32 method, const u32* base_start, u32 amount, virtual void CallMultiMethod(u32 method, const u32* base_start, u32 amount,
u32 methods_pending) = 0; u32 methods_pending) = 0;
void ConsumeSink() {
if (method_sink.empty()) {
return;
}
ConsumeSinkImpl();
}
std::bitset<std::numeric_limits<u16>::max()> execution_mask{};
std::vector<std::pair<u32, u32>> method_sink{};
bool current_dirty{};
GPUVAddr current_dma_segment;
protected:
virtual void ConsumeSinkImpl() {
for (auto [method, value] : method_sink) {
CallMethod(method, value, true);
}
method_sink.clear();
}
}; };
} // namespace Tegra::Engines } // namespace Tegra::Engines

View file

@ -25,6 +25,9 @@ Fermi2D::Fermi2D(MemoryManager& memory_manager_) {
// Nvidia's OpenGL driver seems to assume these values // Nvidia's OpenGL driver seems to assume these values
regs.src.depth = 1; regs.src.depth = 1;
regs.dst.depth = 1; regs.dst.depth = 1;
execution_mask.reset();
execution_mask[FERMI2D_REG_INDEX(pixels_from_memory.src_y0) + 1] = true;
} }
Fermi2D::~Fermi2D() = default; Fermi2D::~Fermi2D() = default;
@ -49,6 +52,13 @@ void Fermi2D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32
} }
} }
void Fermi2D::ConsumeSinkImpl() {
for (auto [method, value] : method_sink) {
regs.reg_array[method] = value;
}
method_sink.clear();
}
void Fermi2D::Blit() { void Fermi2D::Blit() {
MICROPROFILE_SCOPE(GPU_BlitEngine); MICROPROFILE_SCOPE(GPU_BlitEngine);
LOG_DEBUG(HW_GPU, "called. source address=0x{:x}, destination address=0x{:x}", LOG_DEBUG(HW_GPU, "called. source address=0x{:x}, destination address=0x{:x}",

View file

@ -309,6 +309,8 @@ private:
/// Performs the copy from the source surface to the destination surface as configured in the /// Performs the copy from the source surface to the destination surface as configured in the
/// registers. /// registers.
void Blit(); void Blit();
void ConsumeSinkImpl() override;
}; };
#define ASSERT_REG_POSITION(field_name, position) \ #define ASSERT_REG_POSITION(field_name, position) \

View file

@ -14,7 +14,12 @@
namespace Tegra::Engines { namespace Tegra::Engines {
KeplerCompute::KeplerCompute(Core::System& system_, MemoryManager& memory_manager_) KeplerCompute::KeplerCompute(Core::System& system_, MemoryManager& memory_manager_)
: system{system_}, memory_manager{memory_manager_}, upload_state{memory_manager, regs.upload} {} : system{system_}, memory_manager{memory_manager_}, upload_state{memory_manager, regs.upload} {
execution_mask.reset();
execution_mask[KEPLER_COMPUTE_REG_INDEX(exec_upload)] = true;
execution_mask[KEPLER_COMPUTE_REG_INDEX(data_upload)] = true;
execution_mask[KEPLER_COMPUTE_REG_INDEX(launch)] = true;
}
KeplerCompute::~KeplerCompute() = default; KeplerCompute::~KeplerCompute() = default;
@ -23,6 +28,13 @@ void KeplerCompute::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_)
upload_state.BindRasterizer(rasterizer); upload_state.BindRasterizer(rasterizer);
} }
void KeplerCompute::ConsumeSinkImpl() {
for (auto [method, value] : method_sink) {
regs.reg_array[method] = value;
}
method_sink.clear();
}
void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_call) { void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
ASSERT_MSG(method < Regs::NUM_REGS, ASSERT_MSG(method < Regs::NUM_REGS,
"Invalid KeplerCompute register, increase the size of the Regs structure"); "Invalid KeplerCompute register, increase the size of the Regs structure");

View file

@ -204,6 +204,8 @@ public:
private: private:
void ProcessLaunch(); void ProcessLaunch();
void ConsumeSinkImpl() override;
/// Retrieves information about a specific TIC entry from the TIC buffer. /// Retrieves information about a specific TIC entry from the TIC buffer.
Texture::TICEntry GetTICEntry(u32 tic_index) const; Texture::TICEntry GetTICEntry(u32 tic_index) const;

View file

@ -18,6 +18,17 @@ KeplerMemory::~KeplerMemory() = default;
void KeplerMemory::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) { void KeplerMemory::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
upload_state.BindRasterizer(rasterizer_); upload_state.BindRasterizer(rasterizer_);
execution_mask.reset();
execution_mask[KEPLERMEMORY_REG_INDEX(exec)] = true;
execution_mask[KEPLERMEMORY_REG_INDEX(data)] = true;
}
void KeplerMemory::ConsumeSinkImpl() {
for (auto [method, value] : method_sink) {
regs.reg_array[method] = value;
}
method_sink.clear();
} }
void KeplerMemory::CallMethod(u32 method, u32 method_argument, bool is_last_call) { void KeplerMemory::CallMethod(u32 method, u32 method_argument, bool is_last_call) {

View file

@ -73,6 +73,8 @@ public:
} regs{}; } regs{};
private: private:
void ConsumeSinkImpl() override;
Core::System& system; Core::System& system;
Upload::State upload_state; Upload::State upload_state;
}; };

View file

@ -4,6 +4,8 @@
#include <cstring> #include <cstring>
#include <optional> #include <optional>
#include "common/assert.h" #include "common/assert.h"
#include "common/scope_exit.h"
#include "common/settings.h"
#include "core/core.h" #include "core/core.h"
#include "core/core_timing.h" #include "core/core_timing.h"
#include "video_core/dirty_flags.h" #include "video_core/dirty_flags.h"
@ -28,6 +30,10 @@ Maxwell3D::Maxwell3D(Core::System& system_, MemoryManager& memory_manager_)
regs.upload} { regs.upload} {
dirty.flags.flip(); dirty.flags.flip();
InitializeRegisterDefaults(); InitializeRegisterDefaults();
execution_mask.reset();
for (size_t i = 0; i < execution_mask.size(); i++) {
execution_mask[i] = IsMethodExecutable(static_cast<u32>(i));
}
} }
Maxwell3D::~Maxwell3D() = default; Maxwell3D::~Maxwell3D() = default;
@ -121,6 +127,71 @@ void Maxwell3D::InitializeRegisterDefaults() {
shadow_state = regs; shadow_state = regs;
} }
bool Maxwell3D::IsMethodExecutable(u32 method) {
if (method >= MacroRegistersStart) {
return true;
}
switch (method) {
case MAXWELL3D_REG_INDEX(draw.end):
case MAXWELL3D_REG_INDEX(draw.begin):
case MAXWELL3D_REG_INDEX(vertex_buffer.first):
case MAXWELL3D_REG_INDEX(vertex_buffer.count):
case MAXWELL3D_REG_INDEX(index_buffer.first):
case MAXWELL3D_REG_INDEX(index_buffer.count):
case MAXWELL3D_REG_INDEX(draw_inline_index):
case MAXWELL3D_REG_INDEX(index_buffer32_subsequent):
case MAXWELL3D_REG_INDEX(index_buffer16_subsequent):
case MAXWELL3D_REG_INDEX(index_buffer8_subsequent):
case MAXWELL3D_REG_INDEX(index_buffer32_first):
case MAXWELL3D_REG_INDEX(index_buffer16_first):
case MAXWELL3D_REG_INDEX(index_buffer8_first):
case MAXWELL3D_REG_INDEX(inline_index_2x16.even):
case MAXWELL3D_REG_INDEX(inline_index_4x8.index0):
case MAXWELL3D_REG_INDEX(vertex_array_instance_first):
case MAXWELL3D_REG_INDEX(vertex_array_instance_subsequent):
case MAXWELL3D_REG_INDEX(wait_for_idle):
case MAXWELL3D_REG_INDEX(shadow_ram_control):
case MAXWELL3D_REG_INDEX(load_mme.instruction_ptr):
case MAXWELL3D_REG_INDEX(load_mme.instruction):
case MAXWELL3D_REG_INDEX(load_mme.start_address):
case MAXWELL3D_REG_INDEX(falcon[4]):
case MAXWELL3D_REG_INDEX(const_buffer.buffer):
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 1:
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 2:
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 3:
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 4:
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 5:
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 6:
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 7:
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 8:
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 9:
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 10:
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 11:
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 12:
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 13:
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 14:
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 15:
case MAXWELL3D_REG_INDEX(bind_groups[0].raw_config):
case MAXWELL3D_REG_INDEX(bind_groups[1].raw_config):
case MAXWELL3D_REG_INDEX(bind_groups[2].raw_config):
case MAXWELL3D_REG_INDEX(bind_groups[3].raw_config):
case MAXWELL3D_REG_INDEX(bind_groups[4].raw_config):
case MAXWELL3D_REG_INDEX(topology_override):
case MAXWELL3D_REG_INDEX(clear_surface):
case MAXWELL3D_REG_INDEX(report_semaphore.query):
case MAXWELL3D_REG_INDEX(render_enable.mode):
case MAXWELL3D_REG_INDEX(clear_report_value):
case MAXWELL3D_REG_INDEX(sync_info):
case MAXWELL3D_REG_INDEX(launch_dma):
case MAXWELL3D_REG_INDEX(inline_data):
case MAXWELL3D_REG_INDEX(fragment_barrier):
case MAXWELL3D_REG_INDEX(tiled_cache_barrier):
return true;
default:
return false;
}
}
void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) { void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) {
if (executing_macro == 0) { if (executing_macro == 0) {
// A macro call must begin by writing the macro method's register, not its argument. // A macro call must begin by writing the macro method's register, not its argument.
@ -130,14 +201,75 @@ void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool
} }
macro_params.insert(macro_params.end(), base_start, base_start + amount); macro_params.insert(macro_params.end(), base_start, base_start + amount);
for (size_t i = 0; i < amount; i++) {
macro_addresses.push_back(current_dma_segment + i * sizeof(u32));
}
macro_segments.emplace_back(current_dma_segment, amount);
current_macro_dirty |= current_dirty;
current_dirty = false;
// Call the macro when there are no more parameters in the command buffer // Call the macro when there are no more parameters in the command buffer
if (is_last_call) { if (is_last_call) {
ConsumeSink();
CallMacroMethod(executing_macro, macro_params); CallMacroMethod(executing_macro, macro_params);
macro_params.clear(); macro_params.clear();
macro_addresses.clear();
macro_segments.clear();
current_macro_dirty = false;
} }
} }
void Maxwell3D::RefreshParametersImpl() {
if (!Settings::IsGPULevelHigh()) {
return;
}
size_t current_index = 0;
for (auto& segment : macro_segments) {
if (segment.first == 0) {
current_index += segment.second;
continue;
}
memory_manager.ReadBlock(segment.first, &macro_params[current_index],
sizeof(u32) * segment.second);
current_index += segment.second;
}
}
u32 Maxwell3D::GetMaxCurrentVertices() {
u32 num_vertices = 0;
for (size_t index = 0; index < Regs::NumVertexArrays; ++index) {
const auto& array = regs.vertex_streams[index];
if (array.enable == 0) {
continue;
}
const auto& attribute = regs.vertex_attrib_format[index];
if (attribute.constant) {
num_vertices = std::max(num_vertices, 1U);
continue;
}
const auto& limit = regs.vertex_stream_limits[index];
const GPUVAddr gpu_addr_begin = array.Address();
const GPUVAddr gpu_addr_end = limit.Address() + 1;
const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin);
num_vertices = std::max(
num_vertices, address_size / std::max(attribute.SizeInBytes(), array.stride.Value()));
}
return num_vertices;
}
size_t Maxwell3D::EstimateIndexBufferSize() {
GPUVAddr start_address = regs.index_buffer.StartAddress();
GPUVAddr end_address = regs.index_buffer.EndAddress();
constexpr std::array<size_t, 4> max_sizes = {
std::numeric_limits<u8>::max(), std::numeric_limits<u16>::max(),
std::numeric_limits<u32>::max(), std::numeric_limits<u32>::max()};
const size_t byte_size = regs.index_buffer.FormatSizeInBytes();
return std::min<size_t>(
memory_manager.GetMemoryLayoutSize(start_address, byte_size * max_sizes[byte_size]) /
byte_size,
static_cast<size_t>(end_address - start_address));
}
u32 Maxwell3D::ProcessShadowRam(u32 method, u32 argument) { u32 Maxwell3D::ProcessShadowRam(u32 method, u32 argument) {
// Keep track of the register value in shadow_state when requested. // Keep track of the register value in shadow_state when requested.
const auto control = shadow_state.shadow_ram_control; const auto control = shadow_state.shadow_ram_control;
@ -152,6 +284,29 @@ u32 Maxwell3D::ProcessShadowRam(u32 method, u32 argument) {
return argument; return argument;
} }
void Maxwell3D::ConsumeSinkImpl() {
SCOPE_EXIT({ method_sink.clear(); });
const auto control = shadow_state.shadow_ram_control;
if (control == Regs::ShadowRamControl::Track ||
control == Regs::ShadowRamControl::TrackWithFilter) {
for (auto [method, value] : method_sink) {
shadow_state.reg_array[method] = value;
ProcessDirtyRegisters(method, value);
}
return;
}
if (control == Regs::ShadowRamControl::Replay) {
for (auto [method, value] : method_sink) {
ProcessDirtyRegisters(method, shadow_state.reg_array[method]);
}
return;
}
for (auto [method, value] : method_sink) {
ProcessDirtyRegisters(method, value);
}
}
void Maxwell3D::ProcessDirtyRegisters(u32 method, u32 argument) { void Maxwell3D::ProcessDirtyRegisters(u32 method, u32 argument) {
if (regs.reg_array[method] == argument) { if (regs.reg_array[method] == argument) {
return; return;
@ -263,7 +418,6 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
const u32 argument = ProcessShadowRam(method, method_argument); const u32 argument = ProcessShadowRam(method, method_argument);
ProcessDirtyRegisters(method, argument); ProcessDirtyRegisters(method, argument);
ProcessMethodCall(method, argument, method_argument, is_last_call); ProcessMethodCall(method, argument, method_argument, is_last_call);
} }
@ -383,13 +537,17 @@ void Maxwell3D::ProcessQueryGet() {
void Maxwell3D::ProcessQueryCondition() { void Maxwell3D::ProcessQueryCondition() {
const GPUVAddr condition_address{regs.render_enable.Address()}; const GPUVAddr condition_address{regs.render_enable.Address()};
switch (regs.render_enable_override) { switch (regs.render_enable_override) {
case Regs::RenderEnable::Override::AlwaysRender: case Regs::RenderEnable::Override::AlwaysRender: {
execute_on = true; execute_on = true;
break; break;
case Regs::RenderEnable::Override::NeverRender: case Regs::RenderEnable::Override::NeverRender:
execute_on = false; execute_on = false;
break; break;
case Regs::RenderEnable::Override::UseRenderEnable: case Regs::RenderEnable::Override::UseRenderEnable: {
if (rasterizer->AccelerateConditionalRendering()) {
execute_on = true;
return;
}
switch (regs.render_enable.mode) { switch (regs.render_enable.mode) {
case Regs::RenderEnable::Mode::True: { case Regs::RenderEnable::Mode::True: {
execute_on = true; execute_on = true;
@ -427,6 +585,8 @@ void Maxwell3D::ProcessQueryCondition() {
} }
break; break;
} }
}
}
} }
void Maxwell3D::ProcessCounterReset() { void Maxwell3D::ProcessCounterReset() {
@ -463,7 +623,8 @@ std::optional<u64> Maxwell3D::GetQueryResult() {
} }
void Maxwell3D::ProcessCBBind(size_t stage_index) { void Maxwell3D::ProcessCBBind(size_t stage_index) {
// Bind the buffer currently in CB_ADDRESS to the specified index in the desired shader stage. // Bind the buffer currently in CB_ADDRESS to the specified index in the desired shader
// stage.
const auto& bind_data = regs.bind_groups[stage_index]; const auto& bind_data = regs.bind_groups[stage_index];
auto& buffer = state.shader_stages[stage_index].const_buffers[bind_data.shader_slot]; auto& buffer = state.shader_stages[stage_index].const_buffers[bind_data.shader_slot];
buffer.enabled = bind_data.valid.Value() != 0; buffer.enabled = bind_data.valid.Value() != 0;
@ -524,4 +685,9 @@ u32 Maxwell3D::GetRegisterValue(u32 method) const {
return regs.reg_array[method]; return regs.reg_array[method];
} }
void Maxwell3D::setHLEReplacementName(u32 bank, u32 offset, HLEReplaceName name) {
const u64 key = (static_cast<u64>(bank) << 32) | offset;
replace_table.emplace(key, name);
}
} // namespace Tegra::Engines } // namespace Tegra::Engines

View file

@ -272,6 +272,7 @@ public:
}; };
union { union {
u32 raw;
BitField<0, 1, Mode> mode; BitField<0, 1, Mode> mode;
BitField<4, 8, u32> pad; BitField<4, 8, u32> pad;
}; };
@ -1217,10 +1218,12 @@ public:
struct Window { struct Window {
union { union {
u32 raw_1;
BitField<0, 16, u32> x_min; BitField<0, 16, u32> x_min;
BitField<16, 16, u32> x_max; BitField<16, 16, u32> x_max;
}; };
union { union {
u32 raw_2;
BitField<0, 16, u32> y_min; BitField<0, 16, u32> y_min;
BitField<16, 16, u32> y_max; BitField<16, 16, u32> y_max;
}; };
@ -3020,6 +3023,23 @@ public:
/// Store temporary hw register values, used by some calls to restore state after a operation /// Store temporary hw register values, used by some calls to restore state after a operation
Regs shadow_state; Regs shadow_state;
// None Engine
enum class EngineHint : u32 {
None = 0x0,
OnHLEMacro = 0x1,
};
EngineHint engine_state{EngineHint::None};
enum class HLEReplaceName : u32 {
BaseVertex = 0x0,
BaseInstance = 0x1,
};
void setHLEReplacementName(u32 bank, u32 offset, HLEReplaceName name);
std::unordered_map<u64, HLEReplaceName> replace_table;
static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32), "Maxwell3D Regs has wrong size"); static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32), "Maxwell3D Regs has wrong size");
static_assert(std::is_trivially_copyable_v<Regs>, "Maxwell3D Regs must be trivially copyable"); static_assert(std::is_trivially_copyable_v<Regs>, "Maxwell3D Regs must be trivially copyable");
@ -3067,6 +3087,33 @@ public:
std::unique_ptr<DrawManager> draw_manager; std::unique_ptr<DrawManager> draw_manager;
friend class DrawManager; friend class DrawManager;
std::vector<u8> inline_index_draw_indexes;
GPUVAddr getMacroAddress(size_t index) const {
return macro_addresses[index];
}
void RefreshParameters() {
if (!current_macro_dirty) {
return;
}
RefreshParametersImpl();
}
bool AnyParametersDirty() {
return current_macro_dirty;
}
u32 GetMaxCurrentVertices();
size_t EstimateIndexBufferSize();
/// Handles a write to the CLEAR_BUFFERS register.
void ProcessClearBuffers(u32 layer_count);
/// Handles a write to the CB_BIND register.
void ProcessCBBind(size_t stage_index);
private: private:
void InitializeRegisterDefaults(); void InitializeRegisterDefaults();
@ -3076,6 +3123,8 @@ private:
void ProcessDirtyRegisters(u32 method, u32 argument); void ProcessDirtyRegisters(u32 method, u32 argument);
void ConsumeSinkImpl() override;
void ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argument, bool is_last_call); void ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argument, bool is_last_call);
/// Retrieves information about a specific TIC entry from the TIC buffer. /// Retrieves information about a specific TIC entry from the TIC buffer.
@ -3120,12 +3169,13 @@ private:
void ProcessCBData(u32 value); void ProcessCBData(u32 value);
void ProcessCBMultiData(const u32* start_base, u32 amount); void ProcessCBMultiData(const u32* start_base, u32 amount);
/// Handles a write to the CB_BIND register.
void ProcessCBBind(size_t stage_index);
/// Returns a query's value or an empty object if the value will be deferred through a cache. /// Returns a query's value or an empty object if the value will be deferred through a cache.
std::optional<u64> GetQueryResult(); std::optional<u64> GetQueryResult();
void RefreshParametersImpl();
bool IsMethodExecutable(u32 method);
Core::System& system; Core::System& system;
MemoryManager& memory_manager; MemoryManager& memory_manager;
@ -3145,6 +3195,15 @@ private:
Upload::State upload_state; Upload::State upload_state;
bool execute_on{true}; bool execute_on{true};
std::array<bool, Regs::NUM_REGS> draw_command{};
std::vector<u32> deferred_draw_method;
enum class DrawMode : u32 { General = 0, Instance, InlineIndex };
DrawMode draw_mode{DrawMode::General};
bool draw_indexed{};
std::vector<std::pair<GPUVAddr, size_t>> macro_segments;
std::vector<GPUVAddr> macro_addresses;
bool current_macro_dirty{};
}; };
#define ASSERT_REG_POSITION(field_name, position) \ #define ASSERT_REG_POSITION(field_name, position) \

View file

@ -21,7 +21,10 @@ namespace Tegra::Engines {
using namespace Texture; using namespace Texture;
MaxwellDMA::MaxwellDMA(Core::System& system_, MemoryManager& memory_manager_) MaxwellDMA::MaxwellDMA(Core::System& system_, MemoryManager& memory_manager_)
: system{system_}, memory_manager{memory_manager_} {} : system{system_}, memory_manager{memory_manager_} {
execution_mask.reset();
execution_mask[offsetof(Regs, launch_dma) / sizeof(u32)] = true;
}
MaxwellDMA::~MaxwellDMA() = default; MaxwellDMA::~MaxwellDMA() = default;
@ -29,6 +32,13 @@ void MaxwellDMA::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
rasterizer = rasterizer_; rasterizer = rasterizer_;
} }
void MaxwellDMA::ConsumeSinkImpl() {
for (auto [method, value] : method_sink) {
regs.reg_array[method] = value;
}
method_sink.clear();
}
void MaxwellDMA::CallMethod(u32 method, u32 method_argument, bool is_last_call) { void MaxwellDMA::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
ASSERT_MSG(method < NUM_REGS, "Invalid MaxwellDMA register"); ASSERT_MSG(method < NUM_REGS, "Invalid MaxwellDMA register");

View file

@ -231,6 +231,8 @@ private:
void ReleaseSemaphore(); void ReleaseSemaphore();
void ConsumeSinkImpl() override;
Core::System& system; Core::System& system;
MemoryManager& memory_manager; MemoryManager& memory_manager;

View file

@ -12,7 +12,9 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/fs/fs.h" #include "common/fs/fs.h"
#include "common/fs/path_util.h" #include "common/fs/path_util.h"
#include "common/microprofile.h"
#include "common/settings.h" #include "common/settings.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/macro/macro.h" #include "video_core/macro/macro.h"
#include "video_core/macro/macro_hle.h" #include "video_core/macro/macro_hle.h"
#include "video_core/macro/macro_interpreter.h" #include "video_core/macro/macro_interpreter.h"
@ -21,6 +23,8 @@
#include "video_core/macro/macro_jit_x64.h" #include "video_core/macro/macro_jit_x64.h"
#endif #endif
MICROPROFILE_DEFINE(MacroHLE, "GPU", "Execute macro hle", MP_RGB(128, 192, 192));
namespace Tegra { namespace Tegra {
static void Dump(u64 hash, std::span<const u32> code) { static void Dump(u64 hash, std::span<const u32> code) {
@ -40,8 +44,8 @@ static void Dump(u64 hash, std::span<const u32> code) {
macro_file.write(reinterpret_cast<const char*>(code.data()), code.size_bytes()); macro_file.write(reinterpret_cast<const char*>(code.data()), code.size_bytes());
} }
MacroEngine::MacroEngine(Engines::Maxwell3D& maxwell3d) MacroEngine::MacroEngine(Engines::Maxwell3D& maxwell3d_)
: hle_macros{std::make_unique<Tegra::HLEMacro>(maxwell3d)} {} : hle_macros{std::make_unique<Tegra::HLEMacro>(maxwell3d_)}, maxwell3d{maxwell3d_} {}
MacroEngine::~MacroEngine() = default; MacroEngine::~MacroEngine() = default;
@ -59,8 +63,10 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
if (compiled_macro != macro_cache.end()) { if (compiled_macro != macro_cache.end()) {
const auto& cache_info = compiled_macro->second; const auto& cache_info = compiled_macro->second;
if (cache_info.has_hle_program) { if (cache_info.has_hle_program) {
MICROPROFILE_SCOPE(MacroHLE);
cache_info.hle_program->Execute(parameters, method); cache_info.hle_program->Execute(parameters, method);
} else { } else {
maxwell3d.RefreshParameters();
cache_info.lle_program->Execute(parameters, method); cache_info.lle_program->Execute(parameters, method);
} }
} else { } else {
@ -104,8 +110,10 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
if (auto hle_program = hle_macros->GetHLEProgram(cache_info.hash)) { if (auto hle_program = hle_macros->GetHLEProgram(cache_info.hash)) {
cache_info.has_hle_program = true; cache_info.has_hle_program = true;
cache_info.hle_program = std::move(hle_program); cache_info.hle_program = std::move(hle_program);
MICROPROFILE_SCOPE(MacroHLE);
cache_info.hle_program->Execute(parameters, method); cache_info.hle_program->Execute(parameters, method);
} else { } else {
maxwell3d.RefreshParameters();
cache_info.lle_program->Execute(parameters, method); cache_info.lle_program->Execute(parameters, method);
} }
} }

View file

@ -137,6 +137,7 @@ private:
std::unordered_map<u32, CacheInfo> macro_cache; std::unordered_map<u32, CacheInfo> macro_cache;
std::unordered_map<u32, std::vector<u32>> uploaded_macro_code; std::unordered_map<u32, std::vector<u32>> uploaded_macro_code;
std::unique_ptr<HLEMacro> hle_macros; std::unique_ptr<HLEMacro> hle_macros;
Engines::Maxwell3D& maxwell3d;
}; };
std::unique_ptr<MacroEngine> GetMacroEngine(Engines::Maxwell3D& maxwell3d); std::unique_ptr<MacroEngine> GetMacroEngine(Engines::Maxwell3D& maxwell3d);

View file

@ -3,141 +3,451 @@
#include <array> #include <array>
#include <vector> #include <vector>
#include "common/assert.h"
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "video_core/dirty_flags.h" #include "video_core/dirty_flags.h"
#include "video_core/engines/draw_manager.h" #include "video_core/engines/draw_manager.h"
#include "video_core/engines/maxwell_3d.h" #include "video_core/engines/maxwell_3d.h"
#include "video_core/macro/macro.h" #include "video_core/macro/macro.h"
#include "video_core/macro/macro_hle.h" #include "video_core/macro/macro_hle.h"
#include "video_core/memory_manager.h"
#include "video_core/rasterizer_interface.h" #include "video_core/rasterizer_interface.h"
namespace Tegra { namespace Tegra {
using Maxwell = Engines::Maxwell3D;
namespace { namespace {
using HLEFunction = void (*)(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters); bool IsTopologySafe(Maxwell::Regs::PrimitiveTopology topology) {
switch (topology) {
// HLE'd functions case Maxwell::Regs::PrimitiveTopology::Points:
void HLE_771BB18C62444DA0(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) { case Maxwell::Regs::PrimitiveTopology::Lines:
const u32 instance_count = parameters[2] & maxwell3d.GetRegisterValue(0xD1B); case Maxwell::Regs::PrimitiveTopology::LineLoop:
maxwell3d.draw_manager->DrawIndex( case Maxwell::Regs::PrimitiveTopology::LineStrip:
static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0] & 0x3ffffff), case Maxwell::Regs::PrimitiveTopology::Triangles:
parameters[4], parameters[1], parameters[3], parameters[5], instance_count); case Maxwell::Regs::PrimitiveTopology::TriangleStrip:
} case Maxwell::Regs::PrimitiveTopology::TriangleFan:
case Maxwell::Regs::PrimitiveTopology::LinesAdjacency:
void HLE_0D61FC9FAAC9FCAD(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) { case Maxwell::Regs::PrimitiveTopology::LineStripAdjacency:
const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]); case Maxwell::Regs::PrimitiveTopology::TrianglesAdjacency:
maxwell3d.draw_manager->DrawArray( case Maxwell::Regs::PrimitiveTopology::TriangleStripAdjacency:
static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0]), case Maxwell::Regs::PrimitiveTopology::Patches:
parameters[3], parameters[1], parameters[4], instance_count); return true;
} case Maxwell::Regs::PrimitiveTopology::Quads:
case Maxwell::Regs::PrimitiveTopology::QuadStrip:
void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) { case Maxwell::Regs::PrimitiveTopology::Polygon:
const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]); default:
const u32 element_base = parameters[4]; return false;
const u32 base_instance = parameters[5];
maxwell3d.regs.vertex_id_base = element_base;
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
maxwell3d.CallMethod(0x8e3, 0x640, true);
maxwell3d.CallMethod(0x8e4, element_base, true);
maxwell3d.CallMethod(0x8e5, base_instance, true);
maxwell3d.draw_manager->DrawIndex(
static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0]),
parameters[3], parameters[1], element_base, base_instance, instance_count);
maxwell3d.regs.vertex_id_base = 0x0;
maxwell3d.CallMethod(0x8e3, 0x640, true);
maxwell3d.CallMethod(0x8e4, 0x0, true);
maxwell3d.CallMethod(0x8e5, 0x0, true);
}
// Multidraw Indirect
void HLE_3F5E74B9C9A50164(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) {
SCOPE_EXIT({
// Clean everything.
maxwell3d.regs.vertex_id_base = 0x0;
maxwell3d.CallMethod(0x8e3, 0x640, true);
maxwell3d.CallMethod(0x8e4, 0x0, true);
maxwell3d.CallMethod(0x8e5, 0x0, true);
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
});
const u32 start_indirect = parameters[0];
const u32 end_indirect = parameters[1];
if (start_indirect >= end_indirect) {
// Nothing to do.
return;
} }
const u32 padding = parameters[3]; }
const std::size_t max_draws = parameters[4];
const u32 indirect_words = 5 + padding; class HLEMacroImpl : public CachedMacro {
const std::size_t first_draw = start_indirect; public:
const std::size_t effective_draws = end_indirect - start_indirect; explicit HLEMacroImpl(Engines::Maxwell3D& maxwell3d_) : maxwell3d{maxwell3d_} {}
const std::size_t last_draw = start_indirect + std::min(effective_draws, max_draws);
for (std::size_t index = first_draw; index < last_draw; index++) { protected:
const std::size_t base = index * indirect_words + 5; Engines::Maxwell3D& maxwell3d;
const u32 base_vertex = parameters[base + 3]; };
const u32 base_instance = parameters[base + 4];
maxwell3d.regs.vertex_id_base = base_vertex; class HLE_771BB18C62444DA0 final : public HLEMacroImpl {
maxwell3d.CallMethod(0x8e3, 0x640, true); public:
maxwell3d.CallMethod(0x8e4, base_vertex, true); explicit HLE_771BB18C62444DA0(Engines::Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
maxwell3d.CallMethod(0x8e5, base_instance, true);
void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
maxwell3d.RefreshParameters();
const u32 instance_count = parameters[2] & maxwell3d.GetRegisterValue(0xD1B);
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
maxwell3d.draw_manager->DrawIndex( maxwell3d.draw_manager->DrawIndex(
static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[2]), static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0] &
parameters[base + 2], parameters[base], base_vertex, base_instance, 0x3ffffff),
parameters[base + 1]); parameters[4], parameters[1], parameters[3], parameters[5], instance_count);
} }
} };
// Multi-layer Clear class HLE_DrawArraysIndirect final : public HLEMacroImpl {
void HLE_EAD26C3E2109B06B(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) {
ASSERT(parameters.size() == 1);
const Engines::Maxwell3D::Regs::ClearSurface clear_params{parameters[0]};
const u32 rt_index = clear_params.RT;
const u32 num_layers = maxwell3d.regs.rt[rt_index].depth;
ASSERT(clear_params.layer == 0);
maxwell3d.regs.clear_surface.raw = clear_params.raw;
maxwell3d.draw_manager->Clear(num_layers);
}
constexpr std::array<std::pair<u64, HLEFunction>, 5> hle_funcs{{
{0x771BB18C62444DA0, &HLE_771BB18C62444DA0},
{0x0D61FC9FAAC9FCAD, &HLE_0D61FC9FAAC9FCAD},
{0x0217920100488FF7, &HLE_0217920100488FF7},
{0x3F5E74B9C9A50164, &HLE_3F5E74B9C9A50164},
{0xEAD26C3E2109B06B, &HLE_EAD26C3E2109B06B},
}};
class HLEMacroImpl final : public CachedMacro {
public: public:
explicit HLEMacroImpl(Engines::Maxwell3D& maxwell3d_, HLEFunction func_) explicit HLE_DrawArraysIndirect(Engines::Maxwell3D& maxwell3d_, bool extended_ = false)
: maxwell3d{maxwell3d_}, func{func_} {} : HLEMacroImpl(maxwell3d_), extended(extended_) {}
void Execute(const std::vector<u32>& parameters, u32 method) override { void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
func(maxwell3d, parameters); auto topology = static_cast<Maxwell::Regs::PrimitiveTopology>(parameters[0]);
if (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology)) {
Fallback(parameters);
return;
}
auto& params = maxwell3d.draw_manager->GetIndirectParams();
params.is_indexed = false;
params.include_count = false;
params.count_start_address = 0;
params.indirect_start_address = maxwell3d.getMacroAddress(1);
params.buffer_size = 4 * sizeof(u32);
params.max_draw_counts = 1;
params.stride = 0;
if (extended) {
maxwell3d.engine_state = Maxwell::EngineHint::OnHLEMacro;
maxwell3d.setHLEReplacementName(0, 0x640, Maxwell::HLEReplaceName::BaseInstance);
}
maxwell3d.draw_manager->DrawArrayIndirect(topology);
if (extended) {
maxwell3d.engine_state = Maxwell::EngineHint::None;
maxwell3d.replace_table.clear();
}
} }
private: private:
Engines::Maxwell3D& maxwell3d; void Fallback(const std::vector<u32>& parameters) {
HLEFunction func; SCOPE_EXIT({
if (extended) {
maxwell3d.engine_state = Maxwell::EngineHint::None;
maxwell3d.replace_table.clear();
}
});
maxwell3d.RefreshParameters();
const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]);
auto topology = static_cast<Maxwell::Regs::PrimitiveTopology>(parameters[0]);
const u32 vertex_first = parameters[3];
const u32 vertex_count = parameters[1];
if (!IsTopologySafe(topology) &&
static_cast<size_t>(maxwell3d.GetMaxCurrentVertices()) <
static_cast<size_t>(vertex_first) + static_cast<size_t>(vertex_count)) {
ASSERT_MSG(false, "Faulty draw!");
return;
}
const u32 base_instance = parameters[4];
if (extended) {
maxwell3d.regs.global_base_instance_index = base_instance;
maxwell3d.engine_state = Maxwell::EngineHint::OnHLEMacro;
maxwell3d.setHLEReplacementName(0, 0x640, Maxwell::HLEReplaceName::BaseInstance);
}
maxwell3d.draw_manager->DrawArray(topology, vertex_first, vertex_count, base_instance,
instance_count);
if (extended) {
maxwell3d.regs.global_base_instance_index = 0;
maxwell3d.engine_state = Maxwell::EngineHint::None;
maxwell3d.replace_table.clear();
}
}
bool extended;
};
class HLE_DrawIndexedIndirect final : public HLEMacroImpl {
public:
explicit HLE_DrawIndexedIndirect(Engines::Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
auto topology = static_cast<Maxwell::Regs::PrimitiveTopology>(parameters[0]);
if (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology)) {
Fallback(parameters);
return;
}
const u32 estimate = static_cast<u32>(maxwell3d.EstimateIndexBufferSize());
const u32 element_base = parameters[4];
const u32 base_instance = parameters[5];
maxwell3d.regs.vertex_id_base = element_base;
maxwell3d.regs.global_base_vertex_index = element_base;
maxwell3d.regs.global_base_instance_index = base_instance;
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
maxwell3d.engine_state = Maxwell::EngineHint::OnHLEMacro;
maxwell3d.setHLEReplacementName(0, 0x640, Maxwell::HLEReplaceName::BaseVertex);
maxwell3d.setHLEReplacementName(0, 0x644, Maxwell::HLEReplaceName::BaseInstance);
auto& params = maxwell3d.draw_manager->GetIndirectParams();
params.is_indexed = true;
params.include_count = false;
params.count_start_address = 0;
params.indirect_start_address = maxwell3d.getMacroAddress(1);
params.buffer_size = 5 * sizeof(u32);
params.max_draw_counts = 1;
params.stride = 0;
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
maxwell3d.draw_manager->DrawIndexedIndirect(topology, 0, estimate);
maxwell3d.engine_state = Maxwell::EngineHint::None;
maxwell3d.replace_table.clear();
maxwell3d.regs.vertex_id_base = 0x0;
maxwell3d.regs.global_base_vertex_index = 0x0;
maxwell3d.regs.global_base_instance_index = 0x0;
}
private:
void Fallback(const std::vector<u32>& parameters) {
maxwell3d.RefreshParameters();
const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]);
const u32 element_base = parameters[4];
const u32 base_instance = parameters[5];
maxwell3d.regs.vertex_id_base = element_base;
maxwell3d.regs.global_base_vertex_index = element_base;
maxwell3d.regs.global_base_instance_index = base_instance;
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
maxwell3d.engine_state = Maxwell::EngineHint::OnHLEMacro;
maxwell3d.setHLEReplacementName(0, 0x640, Maxwell::HLEReplaceName::BaseVertex);
maxwell3d.setHLEReplacementName(0, 0x644, Maxwell::HLEReplaceName::BaseInstance);
maxwell3d.draw_manager->DrawIndex(
static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0]),
parameters[3], parameters[1], element_base, base_instance, instance_count);
maxwell3d.regs.vertex_id_base = 0x0;
maxwell3d.regs.global_base_vertex_index = 0x0;
maxwell3d.regs.global_base_instance_index = 0x0;
maxwell3d.engine_state = Maxwell::EngineHint::None;
maxwell3d.replace_table.clear();
}
};
class HLE_MultiLayerClear final : public HLEMacroImpl {
public:
explicit HLE_MultiLayerClear(Engines::Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
maxwell3d.RefreshParameters();
ASSERT(parameters.size() == 1);
const Engines::Maxwell3D::Regs::ClearSurface clear_params{parameters[0]};
const u32 rt_index = clear_params.RT;
const u32 num_layers = maxwell3d.regs.rt[rt_index].depth;
ASSERT(clear_params.layer == 0);
maxwell3d.regs.clear_surface.raw = clear_params.raw;
maxwell3d.draw_manager->Clear(num_layers);
}
};
class HLE_MultiDrawIndexedIndirectCount final : public HLEMacroImpl {
public:
explicit HLE_MultiDrawIndexedIndirectCount(Engines::Maxwell3D& maxwell3d_)
: HLEMacroImpl(maxwell3d_) {}
void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
const auto topology = static_cast<Maxwell::Regs::PrimitiveTopology>(parameters[2]);
if (!IsTopologySafe(topology)) {
Fallback(parameters);
return;
}
const u32 start_indirect = parameters[0];
const u32 end_indirect = parameters[1];
if (start_indirect >= end_indirect) {
// Nothing to do.
return;
}
const u32 padding = parameters[3]; // padding is in words
// size of each indirect segment
const u32 indirect_words = 5 + padding;
const u32 stride = indirect_words * sizeof(u32);
const std::size_t draw_count = end_indirect - start_indirect;
const u32 estimate = static_cast<u32>(maxwell3d.EstimateIndexBufferSize());
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
auto& params = maxwell3d.draw_manager->GetIndirectParams();
params.is_indexed = true;
params.include_count = true;
params.count_start_address = maxwell3d.getMacroAddress(4);
params.indirect_start_address = maxwell3d.getMacroAddress(5);
params.buffer_size = stride * draw_count;
params.max_draw_counts = draw_count;
params.stride = stride;
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
maxwell3d.engine_state = Maxwell::EngineHint::OnHLEMacro;
maxwell3d.setHLEReplacementName(0, 0x640, Maxwell::HLEReplaceName::BaseVertex);
maxwell3d.setHLEReplacementName(0, 0x644, Maxwell::HLEReplaceName::BaseInstance);
maxwell3d.draw_manager->DrawIndexedIndirect(topology, 0, estimate);
maxwell3d.engine_state = Maxwell::EngineHint::None;
maxwell3d.replace_table.clear();
}
private:
void Fallback(const std::vector<u32>& parameters) {
SCOPE_EXIT({
// Clean everything.
// Clean everything.
maxwell3d.regs.vertex_id_base = 0x0;
maxwell3d.engine_state = Maxwell::EngineHint::None;
maxwell3d.replace_table.clear();
});
maxwell3d.RefreshParameters();
const u32 start_indirect = parameters[0];
const u32 end_indirect = parameters[1];
if (start_indirect >= end_indirect) {
// Nothing to do.
return;
}
const auto topology = static_cast<Maxwell::Regs::PrimitiveTopology>(parameters[2]);
const u32 padding = parameters[3];
const std::size_t max_draws = parameters[4];
const u32 indirect_words = 5 + padding;
const std::size_t first_draw = start_indirect;
const std::size_t effective_draws = end_indirect - start_indirect;
const std::size_t last_draw = start_indirect + std::min(effective_draws, max_draws);
for (std::size_t index = first_draw; index < last_draw; index++) {
const std::size_t base = index * indirect_words + 5;
const u32 base_vertex = parameters[base + 3];
const u32 base_instance = parameters[base + 4];
maxwell3d.regs.vertex_id_base = base_vertex;
maxwell3d.engine_state = Maxwell::EngineHint::OnHLEMacro;
maxwell3d.setHLEReplacementName(0, 0x640, Maxwell::HLEReplaceName::BaseVertex);
maxwell3d.setHLEReplacementName(0, 0x644, Maxwell::HLEReplaceName::BaseInstance);
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
maxwell3d.draw_manager->DrawIndex(topology, parameters[base + 2], parameters[base],
base_vertex, base_instance, parameters[base + 1]);
}
}
};
class HLE_C713C83D8F63CCF3 final : public HLEMacroImpl {
public:
explicit HLE_C713C83D8F63CCF3(Engines::Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
maxwell3d.RefreshParameters();
const u32 offset = (parameters[0] & 0x3FFFFFFF) << 2;
const u32 address = maxwell3d.regs.shadow_scratch[24];
auto& const_buffer = maxwell3d.regs.const_buffer;
const_buffer.size = 0x7000;
const_buffer.address_high = (address >> 24) & 0xFF;
const_buffer.address_low = address << 8;
const_buffer.offset = offset;
}
};
class HLE_D7333D26E0A93EDE final : public HLEMacroImpl {
public:
explicit HLE_D7333D26E0A93EDE(Engines::Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
maxwell3d.RefreshParameters();
const size_t index = parameters[0];
const u32 address = maxwell3d.regs.shadow_scratch[42 + index];
const u32 size = maxwell3d.regs.shadow_scratch[47 + index];
auto& const_buffer = maxwell3d.regs.const_buffer;
const_buffer.size = size;
const_buffer.address_high = (address >> 24) & 0xFF;
const_buffer.address_low = address << 8;
}
};
class HLE_BindShader final : public HLEMacroImpl {
public:
explicit HLE_BindShader(Engines::Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
maxwell3d.RefreshParameters();
auto& regs = maxwell3d.regs;
const u32 index = parameters[0];
if ((parameters[1] - regs.shadow_scratch[28 + index]) == 0) {
return;
}
regs.pipelines[index & 0xF].offset = parameters[2];
maxwell3d.dirty.flags[VideoCommon::Dirty::Shaders] = true;
regs.shadow_scratch[28 + index] = parameters[1];
regs.shadow_scratch[34 + index] = parameters[2];
const u32 address = parameters[4];
auto& const_buffer = regs.const_buffer;
const_buffer.size = 0x10000;
const_buffer.address_high = (address >> 24) & 0xFF;
const_buffer.address_low = address << 8;
const size_t bind_group_id = parameters[3] & 0x7F;
auto& bind_group = regs.bind_groups[bind_group_id];
bind_group.raw_config = 0x11;
maxwell3d.ProcessCBBind(bind_group_id);
}
};
class HLE_SetRasterBoundingBox final : public HLEMacroImpl {
public:
explicit HLE_SetRasterBoundingBox(Engines::Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
maxwell3d.RefreshParameters();
const u32 raster_mode = parameters[0];
auto& regs = maxwell3d.regs;
const u32 raster_enabled = maxwell3d.regs.conservative_raster_enable;
const u32 scratch_data = maxwell3d.regs.shadow_scratch[52];
regs.raster_bounding_box.raw = raster_mode & 0xFFFFF00F;
regs.raster_bounding_box.pad.Assign(scratch_data & raster_enabled);
}
}; };
} // Anonymous namespace } // Anonymous namespace
HLEMacro::HLEMacro(Engines::Maxwell3D& maxwell3d_) : maxwell3d{maxwell3d_} {} HLEMacro::HLEMacro(Engines::Maxwell3D& maxwell3d_) : maxwell3d{maxwell3d_} {
builders.emplace(0x771BB18C62444DA0ULL,
std::function<std::unique_ptr<CachedMacro>(Engines::Maxwell3D&)>(
[](Engines::Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
return std::make_unique<HLE_771BB18C62444DA0>(maxwell3d__);
}));
builders.emplace(0x0D61FC9FAAC9FCADULL,
std::function<std::unique_ptr<CachedMacro>(Engines::Maxwell3D&)>(
[](Engines::Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
return std::make_unique<HLE_DrawArraysIndirect>(maxwell3d__);
}));
builders.emplace(0x8A4D173EB99A8603ULL,
std::function<std::unique_ptr<CachedMacro>(Engines::Maxwell3D&)>(
[](Engines::Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
return std::make_unique<HLE_DrawArraysIndirect>(maxwell3d__, true);
}));
builders.emplace(0x0217920100488FF7ULL,
std::function<std::unique_ptr<CachedMacro>(Engines::Maxwell3D&)>(
[](Engines::Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
return std::make_unique<HLE_DrawIndexedIndirect>(maxwell3d__);
}));
builders.emplace(0x3F5E74B9C9A50164ULL,
std::function<std::unique_ptr<CachedMacro>(Engines::Maxwell3D&)>(
[](Engines::Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
return std::make_unique<HLE_MultiDrawIndexedIndirectCount>(
maxwell3d__);
}));
builders.emplace(0xEAD26C3E2109B06BULL,
std::function<std::unique_ptr<CachedMacro>(Engines::Maxwell3D&)>(
[](Engines::Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
return std::make_unique<HLE_MultiLayerClear>(maxwell3d__);
}));
builders.emplace(0xC713C83D8F63CCF3ULL,
std::function<std::unique_ptr<CachedMacro>(Engines::Maxwell3D&)>(
[](Engines::Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
return std::make_unique<HLE_C713C83D8F63CCF3>(maxwell3d__);
}));
builders.emplace(0xD7333D26E0A93EDEULL,
std::function<std::unique_ptr<CachedMacro>(Engines::Maxwell3D&)>(
[](Engines::Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
return std::make_unique<HLE_D7333D26E0A93EDE>(maxwell3d__);
}));
builders.emplace(0xEB29B2A09AA06D38ULL,
std::function<std::unique_ptr<CachedMacro>(Engines::Maxwell3D&)>(
[](Engines::Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
return std::make_unique<HLE_BindShader>(maxwell3d__);
}));
builders.emplace(0xDB1341DBEB4C8AF7ULL,
std::function<std::unique_ptr<CachedMacro>(Engines::Maxwell3D&)>(
[](Engines::Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
return std::make_unique<HLE_SetRasterBoundingBox>(maxwell3d__);
}));
}
HLEMacro::~HLEMacro() = default; HLEMacro::~HLEMacro() = default;
std::unique_ptr<CachedMacro> HLEMacro::GetHLEProgram(u64 hash) const { std::unique_ptr<CachedMacro> HLEMacro::GetHLEProgram(u64 hash) const {
const auto it = std::find_if(hle_funcs.cbegin(), hle_funcs.cend(), const auto it = builders.find(hash);
[hash](const auto& pair) { return pair.first == hash; }); if (it == builders.end()) {
if (it == hle_funcs.end()) {
return nullptr; return nullptr;
} }
return std::make_unique<HLEMacroImpl>(maxwell3d, it->second); return it->second(maxwell3d);
} }
} // namespace Tegra } // namespace Tegra

View file

@ -3,7 +3,10 @@
#pragma once #pragma once
#include <functional>
#include <memory> #include <memory>
#include <unordered_map>
#include "common/common_types.h" #include "common/common_types.h"
namespace Tegra { namespace Tegra {
@ -23,6 +26,8 @@ public:
private: private:
Engines::Maxwell3D& maxwell3d; Engines::Maxwell3D& maxwell3d;
std::unordered_map<u64, std::function<std::unique_ptr<CachedMacro>(Engines::Maxwell3D&)>>
builders;
}; };
} // namespace Tegra } // namespace Tegra

View file

@ -25,7 +25,8 @@ MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_, u64
address_space_bits{address_space_bits_}, page_bits{page_bits_}, big_page_bits{big_page_bits_}, address_space_bits{address_space_bits_}, page_bits{page_bits_}, big_page_bits{big_page_bits_},
entries{}, big_entries{}, page_table{address_space_bits, address_space_bits + page_bits - 38, entries{}, big_entries{}, page_table{address_space_bits, address_space_bits + page_bits - 38,
page_bits != big_page_bits ? page_bits : 0}, page_bits != big_page_bits ? page_bits : 0},
unique_identifier{unique_identifier_generator.fetch_add(1, std::memory_order_acq_rel)} { kind_map{PTEKind::INVALID}, unique_identifier{unique_identifier_generator.fetch_add(
1, std::memory_order_acq_rel)} {
address_space_size = 1ULL << address_space_bits; address_space_size = 1ULL << address_space_bits;
page_size = 1ULL << page_bits; page_size = 1ULL << page_bits;
page_mask = page_size - 1ULL; page_mask = page_size - 1ULL;
@ -41,11 +42,7 @@ MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_, u64
big_entries.resize(big_page_table_size / 32, 0); big_entries.resize(big_page_table_size / 32, 0);
big_page_table_cpu.resize(big_page_table_size); big_page_table_cpu.resize(big_page_table_size);
big_page_continous.resize(big_page_table_size / continous_bits, 0); big_page_continous.resize(big_page_table_size / continous_bits, 0);
std::array<PTEKind, 32> kind_valus;
kind_valus.fill(PTEKind::INVALID);
big_kinds.resize(big_page_table_size / 32, kind_valus);
entries.resize(page_table_size / 32, 0); entries.resize(page_table_size / 32, 0);
kinds.resize(page_table_size / 32, kind_valus);
} }
MemoryManager::~MemoryManager() = default; MemoryManager::~MemoryManager() = default;
@ -83,38 +80,7 @@ void MemoryManager::SetEntry(size_t position, MemoryManager::EntryType entry) {
} }
PTEKind MemoryManager::GetPageKind(GPUVAddr gpu_addr) const { PTEKind MemoryManager::GetPageKind(GPUVAddr gpu_addr) const {
auto entry = GetEntry<true>(gpu_addr); return kind_map.GetValueAt(gpu_addr);
if (entry == EntryType::Mapped || entry == EntryType::Reserved) [[likely]] {
return GetKind<true>(gpu_addr);
} else {
return GetKind<false>(gpu_addr);
}
}
template <bool is_big_page>
PTEKind MemoryManager::GetKind(size_t position) const {
if constexpr (is_big_page) {
position = position >> big_page_bits;
const size_t sub_index = position % 32;
return big_kinds[position / 32][sub_index];
} else {
position = position >> page_bits;
const size_t sub_index = position % 32;
return kinds[position / 32][sub_index];
}
}
template <bool is_big_page>
void MemoryManager::SetKind(size_t position, PTEKind kind) {
if constexpr (is_big_page) {
position = position >> big_page_bits;
const size_t sub_index = position % 32;
big_kinds[position / 32][sub_index] = kind;
} else {
position = position >> page_bits;
const size_t sub_index = position % 32;
kinds[position / 32][sub_index] = kind;
}
} }
inline bool MemoryManager::IsBigPageContinous(size_t big_page_index) const { inline bool MemoryManager::IsBigPageContinous(size_t big_page_index) const {
@ -141,7 +107,6 @@ GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cp
const GPUVAddr current_gpu_addr = gpu_addr + offset; const GPUVAddr current_gpu_addr = gpu_addr + offset;
[[maybe_unused]] const auto current_entry_type = GetEntry<false>(current_gpu_addr); [[maybe_unused]] const auto current_entry_type = GetEntry<false>(current_gpu_addr);
SetEntry<false>(current_gpu_addr, entry_type); SetEntry<false>(current_gpu_addr, entry_type);
SetKind<false>(current_gpu_addr, kind);
if (current_entry_type != entry_type) { if (current_entry_type != entry_type) {
rasterizer->ModifyGPUMemory(unique_identifier, gpu_addr, page_size); rasterizer->ModifyGPUMemory(unique_identifier, gpu_addr, page_size);
} }
@ -153,6 +118,7 @@ GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cp
} }
remaining_size -= page_size; remaining_size -= page_size;
} }
kind_map.Map(gpu_addr, gpu_addr + size, kind);
return gpu_addr; return gpu_addr;
} }
@ -164,7 +130,6 @@ GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr
const GPUVAddr current_gpu_addr = gpu_addr + offset; const GPUVAddr current_gpu_addr = gpu_addr + offset;
[[maybe_unused]] const auto current_entry_type = GetEntry<true>(current_gpu_addr); [[maybe_unused]] const auto current_entry_type = GetEntry<true>(current_gpu_addr);
SetEntry<true>(current_gpu_addr, entry_type); SetEntry<true>(current_gpu_addr, entry_type);
SetKind<true>(current_gpu_addr, kind);
if (current_entry_type != entry_type) { if (current_entry_type != entry_type) {
rasterizer->ModifyGPUMemory(unique_identifier, gpu_addr, big_page_size); rasterizer->ModifyGPUMemory(unique_identifier, gpu_addr, big_page_size);
} }
@ -193,6 +158,7 @@ GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr
} }
remaining_size -= big_page_size; remaining_size -= big_page_size;
} }
kind_map.Map(gpu_addr, gpu_addr + size, kind);
return gpu_addr; return gpu_addr;
} }
@ -325,9 +291,15 @@ template <bool is_big_pages, typename FuncMapped, typename FuncReserved, typenam
inline void MemoryManager::MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, inline void MemoryManager::MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size,
FuncMapped&& func_mapped, FuncReserved&& func_reserved, FuncMapped&& func_mapped, FuncReserved&& func_reserved,
FuncUnmapped&& func_unmapped) const { FuncUnmapped&& func_unmapped) const {
static constexpr bool BOOL_BREAK_MAPPED = std::is_same_v<FuncMapped, bool>; using FuncMappedReturn =
static constexpr bool BOOL_BREAK_RESERVED = std::is_same_v<FuncReserved, bool>; typename std::invoke_result<FuncMapped, std::size_t, std::size_t, std::size_t>::type;
static constexpr bool BOOL_BREAK_UNMAPPED = std::is_same_v<FuncUnmapped, bool>; using FuncReservedReturn =
typename std::invoke_result<FuncReserved, std::size_t, std::size_t, std::size_t>::type;
using FuncUnmappedReturn =
typename std::invoke_result<FuncUnmapped, std::size_t, std::size_t, std::size_t>::type;
static constexpr bool BOOL_BREAK_MAPPED = std::is_same_v<FuncMappedReturn, bool>;
static constexpr bool BOOL_BREAK_RESERVED = std::is_same_v<FuncReservedReturn, bool>;
static constexpr bool BOOL_BREAK_UNMAPPED = std::is_same_v<FuncUnmappedReturn, bool>;
u64 used_page_size; u64 used_page_size;
u64 used_page_mask; u64 used_page_mask;
u64 used_page_bits; u64 used_page_bits;
@ -384,8 +356,8 @@ inline void MemoryManager::MemoryOperation(GPUVAddr gpu_src_addr, std::size_t si
} }
template <bool is_safe> template <bool is_safe>
void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,
std::size_t size) const { [[maybe_unused]] VideoCommon::CacheType which) const {
auto set_to_zero = [&]([[maybe_unused]] std::size_t page_index, auto set_to_zero = [&]([[maybe_unused]] std::size_t page_index,
[[maybe_unused]] std::size_t offset, std::size_t copy_amount) { [[maybe_unused]] std::size_t offset, std::size_t copy_amount) {
std::memset(dest_buffer, 0, copy_amount); std::memset(dest_buffer, 0, copy_amount);
@ -395,7 +367,7 @@ void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer,
const VAddr cpu_addr_base = const VAddr cpu_addr_base =
(static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset; (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset;
if constexpr (is_safe) { if constexpr (is_safe) {
rasterizer->FlushRegion(cpu_addr_base, copy_amount); rasterizer->FlushRegion(cpu_addr_base, copy_amount, which);
} }
u8* physical = memory.GetPointer(cpu_addr_base); u8* physical = memory.GetPointer(cpu_addr_base);
std::memcpy(dest_buffer, physical, copy_amount); std::memcpy(dest_buffer, physical, copy_amount);
@ -405,7 +377,7 @@ void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer,
const VAddr cpu_addr_base = const VAddr cpu_addr_base =
(static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset;
if constexpr (is_safe) { if constexpr (is_safe) {
rasterizer->FlushRegion(cpu_addr_base, copy_amount); rasterizer->FlushRegion(cpu_addr_base, copy_amount, which);
} }
if (!IsBigPageContinous(page_index)) [[unlikely]] { if (!IsBigPageContinous(page_index)) [[unlikely]] {
memory.ReadBlockUnsafe(cpu_addr_base, dest_buffer, copy_amount); memory.ReadBlockUnsafe(cpu_addr_base, dest_buffer, copy_amount);
@ -423,18 +395,19 @@ void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer,
MemoryOperation<true>(gpu_src_addr, size, mapped_big, set_to_zero, read_short_pages); MemoryOperation<true>(gpu_src_addr, size, mapped_big, set_to_zero, read_short_pages);
} }
void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const { void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,
ReadBlockImpl<true>(gpu_src_addr, dest_buffer, size); VideoCommon::CacheType which) const {
ReadBlockImpl<true>(gpu_src_addr, dest_buffer, size, which);
} }
void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer, void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer,
const std::size_t size) const { const std::size_t size) const {
ReadBlockImpl<false>(gpu_src_addr, dest_buffer, size); ReadBlockImpl<false>(gpu_src_addr, dest_buffer, size, VideoCommon::CacheType::None);
} }
template <bool is_safe> template <bool is_safe>
void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size,
std::size_t size) { [[maybe_unused]] VideoCommon::CacheType which) {
auto just_advance = [&]([[maybe_unused]] std::size_t page_index, auto just_advance = [&]([[maybe_unused]] std::size_t page_index,
[[maybe_unused]] std::size_t offset, std::size_t copy_amount) { [[maybe_unused]] std::size_t offset, std::size_t copy_amount) {
src_buffer = static_cast<const u8*>(src_buffer) + copy_amount; src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
@ -443,7 +416,7 @@ void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffe
const VAddr cpu_addr_base = const VAddr cpu_addr_base =
(static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset; (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset;
if constexpr (is_safe) { if constexpr (is_safe) {
rasterizer->InvalidateRegion(cpu_addr_base, copy_amount); rasterizer->InvalidateRegion(cpu_addr_base, copy_amount, which);
} }
u8* physical = memory.GetPointer(cpu_addr_base); u8* physical = memory.GetPointer(cpu_addr_base);
std::memcpy(physical, src_buffer, copy_amount); std::memcpy(physical, src_buffer, copy_amount);
@ -453,7 +426,7 @@ void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffe
const VAddr cpu_addr_base = const VAddr cpu_addr_base =
(static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset;
if constexpr (is_safe) { if constexpr (is_safe) {
rasterizer->InvalidateRegion(cpu_addr_base, copy_amount); rasterizer->InvalidateRegion(cpu_addr_base, copy_amount, which);
} }
if (!IsBigPageContinous(page_index)) [[unlikely]] { if (!IsBigPageContinous(page_index)) [[unlikely]] {
memory.WriteBlockUnsafe(cpu_addr_base, src_buffer, copy_amount); memory.WriteBlockUnsafe(cpu_addr_base, src_buffer, copy_amount);
@ -471,16 +444,18 @@ void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffe
MemoryOperation<true>(gpu_dest_addr, size, mapped_big, just_advance, write_short_pages); MemoryOperation<true>(gpu_dest_addr, size, mapped_big, just_advance, write_short_pages);
} }
void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size) { void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size,
WriteBlockImpl<true>(gpu_dest_addr, src_buffer, size); VideoCommon::CacheType which) {
WriteBlockImpl<true>(gpu_dest_addr, src_buffer, size, which);
} }
void MemoryManager::WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer, void MemoryManager::WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer,
std::size_t size) { std::size_t size) {
WriteBlockImpl<false>(gpu_dest_addr, src_buffer, size); WriteBlockImpl<false>(gpu_dest_addr, src_buffer, size, VideoCommon::CacheType::None);
} }
void MemoryManager::FlushRegion(GPUVAddr gpu_addr, size_t size) const { void MemoryManager::FlushRegion(GPUVAddr gpu_addr, size_t size,
VideoCommon::CacheType which) const {
auto do_nothing = [&]([[maybe_unused]] std::size_t page_index, auto do_nothing = [&]([[maybe_unused]] std::size_t page_index,
[[maybe_unused]] std::size_t offset, [[maybe_unused]] std::size_t offset,
[[maybe_unused]] std::size_t copy_amount) {}; [[maybe_unused]] std::size_t copy_amount) {};
@ -488,12 +463,12 @@ void MemoryManager::FlushRegion(GPUVAddr gpu_addr, size_t size) const {
auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
const VAddr cpu_addr_base = const VAddr cpu_addr_base =
(static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset; (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset;
rasterizer->FlushRegion(cpu_addr_base, copy_amount); rasterizer->FlushRegion(cpu_addr_base, copy_amount, which);
}; };
auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
const VAddr cpu_addr_base = const VAddr cpu_addr_base =
(static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset;
rasterizer->FlushRegion(cpu_addr_base, copy_amount); rasterizer->FlushRegion(cpu_addr_base, copy_amount, which);
}; };
auto flush_short_pages = [&](std::size_t page_index, std::size_t offset, auto flush_short_pages = [&](std::size_t page_index, std::size_t offset,
std::size_t copy_amount) { std::size_t copy_amount) {
@ -503,7 +478,8 @@ void MemoryManager::FlushRegion(GPUVAddr gpu_addr, size_t size) const {
MemoryOperation<true>(gpu_addr, size, mapped_big, do_nothing, flush_short_pages); MemoryOperation<true>(gpu_addr, size, mapped_big, do_nothing, flush_short_pages);
} }
bool MemoryManager::IsMemoryDirty(GPUVAddr gpu_addr, size_t size) const { bool MemoryManager::IsMemoryDirty(GPUVAddr gpu_addr, size_t size,
VideoCommon::CacheType which) const {
bool result = false; bool result = false;
auto do_nothing = [&]([[maybe_unused]] std::size_t page_index, auto do_nothing = [&]([[maybe_unused]] std::size_t page_index,
[[maybe_unused]] std::size_t offset, [[maybe_unused]] std::size_t offset,
@ -512,13 +488,13 @@ bool MemoryManager::IsMemoryDirty(GPUVAddr gpu_addr, size_t size) const {
auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
const VAddr cpu_addr_base = const VAddr cpu_addr_base =
(static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset; (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset;
result |= rasterizer->MustFlushRegion(cpu_addr_base, copy_amount); result |= rasterizer->MustFlushRegion(cpu_addr_base, copy_amount, which);
return result; return result;
}; };
auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
const VAddr cpu_addr_base = const VAddr cpu_addr_base =
(static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset;
result |= rasterizer->MustFlushRegion(cpu_addr_base, copy_amount); result |= rasterizer->MustFlushRegion(cpu_addr_base, copy_amount, which);
return result; return result;
}; };
auto check_short_pages = [&](std::size_t page_index, std::size_t offset, auto check_short_pages = [&](std::size_t page_index, std::size_t offset,
@ -571,7 +547,12 @@ size_t MemoryManager::MaxContinousRange(GPUVAddr gpu_addr, size_t size) const {
return range_so_far; return range_so_far;
} }
void MemoryManager::InvalidateRegion(GPUVAddr gpu_addr, size_t size) const { size_t MemoryManager::GetMemoryLayoutSize(GPUVAddr gpu_addr, size_t max_size) const {
return kind_map.GetContinousSizeFrom(gpu_addr);
}
void MemoryManager::InvalidateRegion(GPUVAddr gpu_addr, size_t size,
VideoCommon::CacheType which) const {
auto do_nothing = [&]([[maybe_unused]] std::size_t page_index, auto do_nothing = [&]([[maybe_unused]] std::size_t page_index,
[[maybe_unused]] std::size_t offset, [[maybe_unused]] std::size_t offset,
[[maybe_unused]] std::size_t copy_amount) {}; [[maybe_unused]] std::size_t copy_amount) {};
@ -579,12 +560,12 @@ void MemoryManager::InvalidateRegion(GPUVAddr gpu_addr, size_t size) const {
auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
const VAddr cpu_addr_base = const VAddr cpu_addr_base =
(static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset; (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset;
rasterizer->InvalidateRegion(cpu_addr_base, copy_amount); rasterizer->InvalidateRegion(cpu_addr_base, copy_amount, which);
}; };
auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
const VAddr cpu_addr_base = const VAddr cpu_addr_base =
(static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset;
rasterizer->InvalidateRegion(cpu_addr_base, copy_amount); rasterizer->InvalidateRegion(cpu_addr_base, copy_amount, which);
}; };
auto invalidate_short_pages = [&](std::size_t page_index, std::size_t offset, auto invalidate_short_pages = [&](std::size_t page_index, std::size_t offset,
std::size_t copy_amount) { std::size_t copy_amount) {
@ -594,14 +575,15 @@ void MemoryManager::InvalidateRegion(GPUVAddr gpu_addr, size_t size) const {
MemoryOperation<true>(gpu_addr, size, mapped_big, do_nothing, invalidate_short_pages); MemoryOperation<true>(gpu_addr, size, mapped_big, do_nothing, invalidate_short_pages);
} }
void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size) { void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size,
VideoCommon::CacheType which) {
std::vector<u8> tmp_buffer(size); std::vector<u8> tmp_buffer(size);
ReadBlock(gpu_src_addr, tmp_buffer.data(), size); ReadBlock(gpu_src_addr, tmp_buffer.data(), size, which);
// The output block must be flushed in case it has data modified from the GPU. // The output block must be flushed in case it has data modified from the GPU.
// Fixes NPC geometry in Zombie Panic in Wonderland DX // Fixes NPC geometry in Zombie Panic in Wonderland DX
FlushRegion(gpu_dest_addr, size); FlushRegion(gpu_dest_addr, size, which);
WriteBlock(gpu_dest_addr, tmp_buffer.data(), size); WriteBlock(gpu_dest_addr, tmp_buffer.data(), size, which);
} }
bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const { bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const {

View file

@ -10,7 +10,9 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "common/multi_level_page_table.h" #include "common/multi_level_page_table.h"
#include "common/range_map.h"
#include "common/virtual_buffer.h" #include "common/virtual_buffer.h"
#include "video_core/cache_types.h"
#include "video_core/pte_kind.h" #include "video_core/pte_kind.h"
namespace VideoCore { namespace VideoCore {
@ -59,9 +61,12 @@ public:
* in the Host Memory counterpart. Note: This functions cause Host GPU Memory * in the Host Memory counterpart. Note: This functions cause Host GPU Memory
* Flushes and Invalidations, respectively to each operation. * Flushes and Invalidations, respectively to each operation.
*/ */
void ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const; void ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,
void WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size); VideoCommon::CacheType which = VideoCommon::CacheType::All) const;
void CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size); void WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size,
VideoCommon::CacheType which = VideoCommon::CacheType::All);
void CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size,
VideoCommon::CacheType which = VideoCommon::CacheType::All);
/** /**
* ReadBlockUnsafe and WriteBlockUnsafe are special versions of ReadBlock and * ReadBlockUnsafe and WriteBlockUnsafe are special versions of ReadBlock and
@ -104,11 +109,14 @@ public:
GPUVAddr MapSparse(GPUVAddr gpu_addr, std::size_t size, bool is_big_pages = true); GPUVAddr MapSparse(GPUVAddr gpu_addr, std::size_t size, bool is_big_pages = true);
void Unmap(GPUVAddr gpu_addr, std::size_t size); void Unmap(GPUVAddr gpu_addr, std::size_t size);
void FlushRegion(GPUVAddr gpu_addr, size_t size) const; void FlushRegion(GPUVAddr gpu_addr, size_t size,
VideoCommon::CacheType which = VideoCommon::CacheType::All) const;
void InvalidateRegion(GPUVAddr gpu_addr, size_t size) const; void InvalidateRegion(GPUVAddr gpu_addr, size_t size,
VideoCommon::CacheType which = VideoCommon::CacheType::All) const;
bool IsMemoryDirty(GPUVAddr gpu_addr, size_t size) const; bool IsMemoryDirty(GPUVAddr gpu_addr, size_t size,
VideoCommon::CacheType which = VideoCommon::CacheType::All) const;
size_t MaxContinousRange(GPUVAddr gpu_addr, size_t size) const; size_t MaxContinousRange(GPUVAddr gpu_addr, size_t size) const;
@ -118,16 +126,21 @@ public:
PTEKind GetPageKind(GPUVAddr gpu_addr) const; PTEKind GetPageKind(GPUVAddr gpu_addr) const;
size_t GetMemoryLayoutSize(GPUVAddr gpu_addr,
size_t max_size = std::numeric_limits<size_t>::max()) const;
private: private:
template <bool is_big_pages, typename FuncMapped, typename FuncReserved, typename FuncUnmapped> template <bool is_big_pages, typename FuncMapped, typename FuncReserved, typename FuncUnmapped>
inline void MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, FuncMapped&& func_mapped, inline void MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, FuncMapped&& func_mapped,
FuncReserved&& func_reserved, FuncUnmapped&& func_unmapped) const; FuncReserved&& func_reserved, FuncUnmapped&& func_unmapped) const;
template <bool is_safe> template <bool is_safe>
void ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const; void ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,
VideoCommon::CacheType which) const;
template <bool is_safe> template <bool is_safe>
void WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size); void WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size,
VideoCommon::CacheType which);
template <bool is_big_page> template <bool is_big_page>
[[nodiscard]] std::size_t PageEntryIndex(GPUVAddr gpu_addr) const { [[nodiscard]] std::size_t PageEntryIndex(GPUVAddr gpu_addr) const {
@ -183,16 +196,8 @@ private:
template <bool is_big_page> template <bool is_big_page>
inline void SetEntry(size_t position, EntryType entry); inline void SetEntry(size_t position, EntryType entry);
std::vector<std::array<PTEKind, 32>> kinds;
std::vector<std::array<PTEKind, 32>> big_kinds;
template <bool is_big_page>
inline PTEKind GetKind(size_t position) const;
template <bool is_big_page>
inline void SetKind(size_t position, PTEKind kind);
Common::MultiLevelPageTable<u32> page_table; Common::MultiLevelPageTable<u32> page_table;
Common::RangeMap<GPUVAddr, PTEKind> kind_map;
Common::VirtualBuffer<u32> big_page_table_cpu; Common::VirtualBuffer<u32> big_page_table_cpu;
std::vector<u64> big_page_continous; std::vector<u64> big_page_continous;

View file

@ -8,6 +8,7 @@
#include <span> #include <span>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/polyfill_thread.h" #include "common/polyfill_thread.h"
#include "video_core/cache_types.h"
#include "video_core/engines/fermi_2d.h" #include "video_core/engines/fermi_2d.h"
#include "video_core/gpu.h" #include "video_core/gpu.h"
@ -42,6 +43,9 @@ public:
/// Dispatches a draw invocation /// Dispatches a draw invocation
virtual void Draw(bool is_indexed, u32 instance_count) = 0; virtual void Draw(bool is_indexed, u32 instance_count) = 0;
/// Dispatches an indirect draw invocation
virtual void DrawIndirect() {}
/// Clear the current framebuffer /// Clear the current framebuffer
virtual void Clear(u32 layer_count) = 0; virtual void Clear(u32 layer_count) = 0;
@ -80,13 +84,16 @@ public:
virtual void FlushAll() = 0; virtual void FlushAll() = 0;
/// Notify rasterizer that any caches of the specified region should be flushed to Switch memory /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
virtual void FlushRegion(VAddr addr, u64 size) = 0; virtual void FlushRegion(VAddr addr, u64 size,
VideoCommon::CacheType which = VideoCommon::CacheType::All) = 0;
/// Check if the the specified memory area requires flushing to CPU Memory. /// Check if the the specified memory area requires flushing to CPU Memory.
virtual bool MustFlushRegion(VAddr addr, u64 size) = 0; virtual bool MustFlushRegion(VAddr addr, u64 size,
VideoCommon::CacheType which = VideoCommon::CacheType::All) = 0;
/// Notify rasterizer that any caches of the specified region should be invalidated /// Notify rasterizer that any caches of the specified region should be invalidated
virtual void InvalidateRegion(VAddr addr, u64 size) = 0; virtual void InvalidateRegion(VAddr addr, u64 size,
VideoCommon::CacheType which = VideoCommon::CacheType::All) = 0;
/// Notify rasterizer that any caches of the specified region are desync with guest /// Notify rasterizer that any caches of the specified region are desync with guest
virtual void OnCPUWrite(VAddr addr, u64 size) = 0; virtual void OnCPUWrite(VAddr addr, u64 size) = 0;
@ -102,7 +109,8 @@ public:
/// Notify rasterizer that any caches of the specified region should be flushed to Switch memory /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
/// and invalidated /// and invalidated
virtual void FlushAndInvalidateRegion(VAddr addr, u64 size) = 0; virtual void FlushAndInvalidateRegion(
VAddr addr, u64 size, VideoCommon::CacheType which = VideoCommon::CacheType::All) = 0;
/// Notify the host renderer to wait for previous primitive and compute operations. /// Notify the host renderer to wait for previous primitive and compute operations.
virtual void WaitForIdle() = 0; virtual void WaitForIdle() = 0;
@ -119,6 +127,10 @@ public:
/// Notify rasterizer that a frame is about to finish /// Notify rasterizer that a frame is about to finish
virtual void TickFrame() = 0; virtual void TickFrame() = 0;
virtual bool AccelerateConditionalRendering() {
return false;
}
/// Attempt to use a faster method to perform a surface copy /// Attempt to use a faster method to perform a surface copy
[[nodiscard]] virtual bool AccelerateSurfaceCopy( [[nodiscard]] virtual bool AccelerateSurfaceCopy(
const Tegra::Engines::Fermi2D::Surface& src, const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Surface& src, const Tegra::Engines::Fermi2D::Surface& dst,

View file

@ -39,11 +39,11 @@ void RasterizerNull::BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr
u32 size) {} u32 size) {}
void RasterizerNull::DisableGraphicsUniformBuffer(size_t stage, u32 index) {} void RasterizerNull::DisableGraphicsUniformBuffer(size_t stage, u32 index) {}
void RasterizerNull::FlushAll() {} void RasterizerNull::FlushAll() {}
void RasterizerNull::FlushRegion(VAddr addr, u64 size) {} void RasterizerNull::FlushRegion(VAddr addr, u64 size, VideoCommon::CacheType) {}
bool RasterizerNull::MustFlushRegion(VAddr addr, u64 size) { bool RasterizerNull::MustFlushRegion(VAddr addr, u64 size, VideoCommon::CacheType) {
return false; return false;
} }
void RasterizerNull::InvalidateRegion(VAddr addr, u64 size) {} void RasterizerNull::InvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType) {}
void RasterizerNull::OnCPUWrite(VAddr addr, u64 size) {} void RasterizerNull::OnCPUWrite(VAddr addr, u64 size) {}
void RasterizerNull::InvalidateGPUCache() {} void RasterizerNull::InvalidateGPUCache() {}
void RasterizerNull::UnmapMemory(VAddr addr, u64 size) {} void RasterizerNull::UnmapMemory(VAddr addr, u64 size) {}
@ -61,7 +61,7 @@ void RasterizerNull::SignalSyncPoint(u32 value) {
} }
void RasterizerNull::SignalReference() {} void RasterizerNull::SignalReference() {}
void RasterizerNull::ReleaseFences() {} void RasterizerNull::ReleaseFences() {}
void RasterizerNull::FlushAndInvalidateRegion(VAddr addr, u64 size) {} void RasterizerNull::FlushAndInvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType) {}
void RasterizerNull::WaitForIdle() {} void RasterizerNull::WaitForIdle() {}
void RasterizerNull::FragmentBarrier() {} void RasterizerNull::FragmentBarrier() {}
void RasterizerNull::TiledCacheBarrier() {} void RasterizerNull::TiledCacheBarrier() {}

View file

@ -38,9 +38,12 @@ public:
void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override; void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override;
void DisableGraphicsUniformBuffer(size_t stage, u32 index) override; void DisableGraphicsUniformBuffer(size_t stage, u32 index) override;
void FlushAll() override; void FlushAll() override;
void FlushRegion(VAddr addr, u64 size) override; void FlushRegion(VAddr addr, u64 size,
bool MustFlushRegion(VAddr addr, u64 size) override; VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
void InvalidateRegion(VAddr addr, u64 size) override; bool MustFlushRegion(VAddr addr, u64 size,
VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
void InvalidateRegion(VAddr addr, u64 size,
VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
void OnCPUWrite(VAddr addr, u64 size) override; void OnCPUWrite(VAddr addr, u64 size) override;
void InvalidateGPUCache() override; void InvalidateGPUCache() override;
void UnmapMemory(VAddr addr, u64 size) override; void UnmapMemory(VAddr addr, u64 size) override;
@ -50,7 +53,8 @@ public:
void SignalSyncPoint(u32 value) override; void SignalSyncPoint(u32 value) override;
void SignalReference() override; void SignalReference() override;
void ReleaseFences() override; void ReleaseFences() override;
void FlushAndInvalidateRegion(VAddr addr, u64 size) override; void FlushAndInvalidateRegion(
VAddr addr, u64 size, VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
void WaitForIdle() override; void WaitForIdle() override;
void FragmentBarrier() override; void FragmentBarrier() override;
void TiledCacheBarrier() override; void TiledCacheBarrier() override;

View file

@ -40,6 +40,7 @@ struct GraphicsPipelineKey {
BitField<6, 2, Maxwell::Tessellation::DomainType> tessellation_primitive; BitField<6, 2, Maxwell::Tessellation::DomainType> tessellation_primitive;
BitField<8, 2, Maxwell::Tessellation::Spacing> tessellation_spacing; BitField<8, 2, Maxwell::Tessellation::Spacing> tessellation_spacing;
BitField<10, 1, u32> tessellation_clockwise; BitField<10, 1, u32> tessellation_clockwise;
BitField<11, 3, Tegra::Engines::Maxwell3D::EngineHint> app_stage;
}; };
std::array<u32, 3> padding; std::array<u32, 3> padding;
VideoCommon::TransformFeedbackState xfb_state; VideoCommon::TransformFeedbackState xfb_state;

View file

@ -202,7 +202,8 @@ void RasterizerOpenGL::Clear(u32 layer_count) {
++num_queued_commands; ++num_queued_commands;
} }
void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) { template <typename Func>
void RasterizerOpenGL::PrepareDraw(bool is_indexed, Func&& draw_func) {
MICROPROFILE_SCOPE(OpenGL_Drawing); MICROPROFILE_SCOPE(OpenGL_Drawing);
SCOPE_EXIT({ gpu.TickWork(); }); SCOPE_EXIT({ gpu.TickWork(); });
@ -226,48 +227,97 @@ void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) {
const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(draw_state.topology); const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(draw_state.topology);
BeginTransformFeedback(pipeline, primitive_mode); BeginTransformFeedback(pipeline, primitive_mode);
const GLuint base_instance = static_cast<GLuint>(draw_state.base_instance); draw_func(primitive_mode);
const GLsizei num_instances = static_cast<GLsizei>(instance_count);
if (is_indexed) {
const GLint base_vertex = static_cast<GLint>(draw_state.base_index);
const GLsizei num_vertices = static_cast<GLsizei>(draw_state.index_buffer.count);
const GLvoid* const offset = buffer_cache_runtime.IndexOffset();
const GLenum format = MaxwellToGL::IndexFormat(draw_state.index_buffer.format);
if (num_instances == 1 && base_instance == 0 && base_vertex == 0) {
glDrawElements(primitive_mode, num_vertices, format, offset);
} else if (num_instances == 1 && base_instance == 0) {
glDrawElementsBaseVertex(primitive_mode, num_vertices, format, offset, base_vertex);
} else if (base_vertex == 0 && base_instance == 0) {
glDrawElementsInstanced(primitive_mode, num_vertices, format, offset, num_instances);
} else if (base_vertex == 0) {
glDrawElementsInstancedBaseInstance(primitive_mode, num_vertices, format, offset,
num_instances, base_instance);
} else if (base_instance == 0) {
glDrawElementsInstancedBaseVertex(primitive_mode, num_vertices, format, offset,
num_instances, base_vertex);
} else {
glDrawElementsInstancedBaseVertexBaseInstance(primitive_mode, num_vertices, format,
offset, num_instances, base_vertex,
base_instance);
}
} else {
const GLint base_vertex = static_cast<GLint>(draw_state.vertex_buffer.first);
const GLsizei num_vertices = static_cast<GLsizei>(draw_state.vertex_buffer.count);
if (num_instances == 1 && base_instance == 0) {
glDrawArrays(primitive_mode, base_vertex, num_vertices);
} else if (base_instance == 0) {
glDrawArraysInstanced(primitive_mode, base_vertex, num_vertices, num_instances);
} else {
glDrawArraysInstancedBaseInstance(primitive_mode, base_vertex, num_vertices,
num_instances, base_instance);
}
}
EndTransformFeedback(); EndTransformFeedback();
++num_queued_commands; ++num_queued_commands;
has_written_global_memory |= pipeline->WritesGlobalMemory(); has_written_global_memory |= pipeline->WritesGlobalMemory();
} }
void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) {
PrepareDraw(is_indexed, [this, is_indexed, instance_count](GLenum primitive_mode) {
const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
const GLuint base_instance = static_cast<GLuint>(draw_state.base_instance);
const GLsizei num_instances = static_cast<GLsizei>(instance_count);
if (is_indexed) {
const GLint base_vertex = static_cast<GLint>(draw_state.base_index);
const GLsizei num_vertices = static_cast<GLsizei>(draw_state.index_buffer.count);
const GLvoid* const offset = buffer_cache_runtime.IndexOffset();
const GLenum format = MaxwellToGL::IndexFormat(draw_state.index_buffer.format);
if (num_instances == 1 && base_instance == 0 && base_vertex == 0) {
glDrawElements(primitive_mode, num_vertices, format, offset);
} else if (num_instances == 1 && base_instance == 0) {
glDrawElementsBaseVertex(primitive_mode, num_vertices, format, offset, base_vertex);
} else if (base_vertex == 0 && base_instance == 0) {
glDrawElementsInstanced(primitive_mode, num_vertices, format, offset,
num_instances);
} else if (base_vertex == 0) {
glDrawElementsInstancedBaseInstance(primitive_mode, num_vertices, format, offset,
num_instances, base_instance);
} else if (base_instance == 0) {
glDrawElementsInstancedBaseVertex(primitive_mode, num_vertices, format, offset,
num_instances, base_vertex);
} else {
glDrawElementsInstancedBaseVertexBaseInstance(primitive_mode, num_vertices, format,
offset, num_instances, base_vertex,
base_instance);
}
} else {
const GLint base_vertex = static_cast<GLint>(draw_state.vertex_buffer.first);
const GLsizei num_vertices = static_cast<GLsizei>(draw_state.vertex_buffer.count);
if (num_instances == 1 && base_instance == 0) {
glDrawArrays(primitive_mode, base_vertex, num_vertices);
} else if (base_instance == 0) {
glDrawArraysInstanced(primitive_mode, base_vertex, num_vertices, num_instances);
} else {
glDrawArraysInstancedBaseInstance(primitive_mode, base_vertex, num_vertices,
num_instances, base_instance);
}
}
});
}
void RasterizerOpenGL::DrawIndirect() {
const auto& params = maxwell3d->draw_manager->GetIndirectParams();
buffer_cache.SetDrawIndirect(&params);
PrepareDraw(params.is_indexed, [this, &params](GLenum primitive_mode) {
const auto [buffer, offset] = buffer_cache.GetDrawIndirectBuffer();
const GLvoid* const gl_offset =
reinterpret_cast<const GLvoid*>(static_cast<uintptr_t>(offset));
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, buffer->Handle());
if (params.include_count) {
const auto [draw_buffer, offset_base] = buffer_cache.GetDrawIndirectCount();
glBindBuffer(GL_PARAMETER_BUFFER, draw_buffer->Handle());
if (params.is_indexed) {
const GLenum format = MaxwellToGL::IndexFormat(maxwell3d->regs.index_buffer.format);
glMultiDrawElementsIndirectCount(primitive_mode, format, gl_offset,
static_cast<GLintptr>(offset_base),
static_cast<GLsizei>(params.max_draw_counts),
static_cast<GLsizei>(params.stride));
} else {
glMultiDrawArraysIndirectCount(primitive_mode, gl_offset,
static_cast<GLintptr>(offset_base),
static_cast<GLsizei>(params.max_draw_counts),
static_cast<GLsizei>(params.stride));
}
return;
}
if (params.is_indexed) {
const GLenum format = MaxwellToGL::IndexFormat(maxwell3d->regs.index_buffer.format);
glMultiDrawElementsIndirect(primitive_mode, format, gl_offset,
static_cast<GLsizei>(params.max_draw_counts),
static_cast<GLsizei>(params.stride));
} else {
glMultiDrawArraysIndirect(primitive_mode, gl_offset,
static_cast<GLsizei>(params.max_draw_counts),
static_cast<GLsizei>(params.stride));
}
});
buffer_cache.SetDrawIndirect(nullptr);
}
void RasterizerOpenGL::DispatchCompute() { void RasterizerOpenGL::DispatchCompute() {
ComputePipeline* const pipeline{shader_cache.CurrentComputePipeline()}; ComputePipeline* const pipeline{shader_cache.CurrentComputePipeline()};
if (!pipeline) { if (!pipeline) {
@ -302,46 +352,60 @@ void RasterizerOpenGL::DisableGraphicsUniformBuffer(size_t stage, u32 index) {
void RasterizerOpenGL::FlushAll() {} void RasterizerOpenGL::FlushAll() {}
void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) { void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size, VideoCommon::CacheType which) {
MICROPROFILE_SCOPE(OpenGL_CacheManagement); MICROPROFILE_SCOPE(OpenGL_CacheManagement);
if (addr == 0 || size == 0) { if (addr == 0 || size == 0) {
return; return;
} }
{ if (bool(which & VideoCommon::CacheType::TextureCache)) {
std::scoped_lock lock{texture_cache.mutex}; std::scoped_lock lock{texture_cache.mutex};
texture_cache.DownloadMemory(addr, size); texture_cache.DownloadMemory(addr, size);
} }
{ if ((bool(which & VideoCommon::CacheType::BufferCache))) {
std::scoped_lock lock{buffer_cache.mutex}; std::scoped_lock lock{buffer_cache.mutex};
buffer_cache.DownloadMemory(addr, size); buffer_cache.DownloadMemory(addr, size);
} }
query_cache.FlushRegion(addr, size); if ((bool(which & VideoCommon::CacheType::QueryCache))) {
} query_cache.FlushRegion(addr, size);
bool RasterizerOpenGL::MustFlushRegion(VAddr addr, u64 size) {
std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
if (!Settings::IsGPULevelHigh()) {
return buffer_cache.IsRegionGpuModified(addr, size);
} }
return texture_cache.IsRegionGpuModified(addr, size) ||
buffer_cache.IsRegionGpuModified(addr, size);
} }
void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) { bool RasterizerOpenGL::MustFlushRegion(VAddr addr, u64 size, VideoCommon::CacheType which) {
if ((bool(which & VideoCommon::CacheType::BufferCache))) {
std::scoped_lock lock{buffer_cache.mutex};
if (buffer_cache.IsRegionGpuModified(addr, size)) {
return true;
}
}
if (!Settings::IsGPULevelHigh()) {
return false;
}
if (bool(which & VideoCommon::CacheType::TextureCache)) {
std::scoped_lock lock{texture_cache.mutex};
return texture_cache.IsRegionGpuModified(addr, size);
}
return false;
}
void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType which) {
MICROPROFILE_SCOPE(OpenGL_CacheManagement); MICROPROFILE_SCOPE(OpenGL_CacheManagement);
if (addr == 0 || size == 0) { if (addr == 0 || size == 0) {
return; return;
} }
{ if (bool(which & VideoCommon::CacheType::TextureCache)) {
std::scoped_lock lock{texture_cache.mutex}; std::scoped_lock lock{texture_cache.mutex};
texture_cache.WriteMemory(addr, size); texture_cache.WriteMemory(addr, size);
} }
{ if (bool(which & VideoCommon::CacheType::BufferCache)) {
std::scoped_lock lock{buffer_cache.mutex}; std::scoped_lock lock{buffer_cache.mutex};
buffer_cache.WriteMemory(addr, size); buffer_cache.WriteMemory(addr, size);
} }
shader_cache.InvalidateRegion(addr, size); if (bool(which & VideoCommon::CacheType::ShaderCache)) {
query_cache.InvalidateRegion(addr, size); shader_cache.InvalidateRegion(addr, size);
}
if (bool(which & VideoCommon::CacheType::QueryCache)) {
query_cache.InvalidateRegion(addr, size);
}
} }
void RasterizerOpenGL::OnCPUWrite(VAddr addr, u64 size) { void RasterizerOpenGL::OnCPUWrite(VAddr addr, u64 size) {
@ -408,11 +472,12 @@ void RasterizerOpenGL::ReleaseFences() {
fence_manager.WaitPendingFences(); fence_manager.WaitPendingFences();
} }
void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) { void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size,
VideoCommon::CacheType which) {
if (Settings::IsGPULevelExtreme()) { if (Settings::IsGPULevelExtreme()) {
FlushRegion(addr, size); FlushRegion(addr, size, which);
} }
InvalidateRegion(addr, size); InvalidateRegion(addr, size, which);
} }
void RasterizerOpenGL::WaitForIdle() { void RasterizerOpenGL::WaitForIdle() {
@ -460,6 +525,21 @@ void RasterizerOpenGL::TickFrame() {
} }
} }
bool RasterizerOpenGL::AccelerateConditionalRendering() {
if (Settings::IsGPULevelHigh()) {
// Reimplement Host conditional rendering.
return false;
}
// Medium / Low Hack: stub any checks on queries writen into the buffer cache.
const GPUVAddr condition_address{maxwell3d->regs.render_enable.Address()};
Maxwell::ReportSemaphore::Compare cmp;
if (gpu_memory->IsMemoryDirty(condition_address, sizeof(cmp),
VideoCommon::CacheType::BufferCache)) {
return true;
}
return false;
}
bool RasterizerOpenGL::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, bool RasterizerOpenGL::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src,
const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Surface& dst,
const Tegra::Engines::Fermi2D::Config& copy_config) { const Tegra::Engines::Fermi2D::Config& copy_config) {
@ -481,7 +561,7 @@ void RasterizerOpenGL::AccelerateInlineToMemory(GPUVAddr address, size_t copy_si
} }
gpu_memory->WriteBlockUnsafe(address, memory.data(), copy_size); gpu_memory->WriteBlockUnsafe(address, memory.data(), copy_size);
{ {
std::unique_lock<std::mutex> lock{buffer_cache.mutex}; std::unique_lock<std::recursive_mutex> lock{buffer_cache.mutex};
if (!buffer_cache.InlineMemory(*cpu_addr, copy_size, memory)) { if (!buffer_cache.InlineMemory(*cpu_addr, copy_size, memory)) {
buffer_cache.WriteMemory(*cpu_addr, copy_size); buffer_cache.WriteMemory(*cpu_addr, copy_size);
} }

View file

@ -69,6 +69,7 @@ public:
~RasterizerOpenGL() override; ~RasterizerOpenGL() override;
void Draw(bool is_indexed, u32 instance_count) override; void Draw(bool is_indexed, u32 instance_count) override;
void DrawIndirect() override;
void Clear(u32 layer_count) override; void Clear(u32 layer_count) override;
void DispatchCompute() override; void DispatchCompute() override;
void ResetCounter(VideoCore::QueryType type) override; void ResetCounter(VideoCore::QueryType type) override;
@ -76,9 +77,12 @@ public:
void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override; void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override;
void DisableGraphicsUniformBuffer(size_t stage, u32 index) override; void DisableGraphicsUniformBuffer(size_t stage, u32 index) override;
void FlushAll() override; void FlushAll() override;
void FlushRegion(VAddr addr, u64 size) override; void FlushRegion(VAddr addr, u64 size,
bool MustFlushRegion(VAddr addr, u64 size) override; VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
void InvalidateRegion(VAddr addr, u64 size) override; bool MustFlushRegion(VAddr addr, u64 size,
VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
void InvalidateRegion(VAddr addr, u64 size,
VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
void OnCPUWrite(VAddr addr, u64 size) override; void OnCPUWrite(VAddr addr, u64 size) override;
void InvalidateGPUCache() override; void InvalidateGPUCache() override;
void UnmapMemory(VAddr addr, u64 size) override; void UnmapMemory(VAddr addr, u64 size) override;
@ -88,12 +92,14 @@ public:
void SignalSyncPoint(u32 value) override; void SignalSyncPoint(u32 value) override;
void SignalReference() override; void SignalReference() override;
void ReleaseFences() override; void ReleaseFences() override;
void FlushAndInvalidateRegion(VAddr addr, u64 size) override; void FlushAndInvalidateRegion(
VAddr addr, u64 size, VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
void WaitForIdle() override; void WaitForIdle() override;
void FragmentBarrier() override; void FragmentBarrier() override;
void TiledCacheBarrier() override; void TiledCacheBarrier() override;
void FlushCommands() override; void FlushCommands() override;
void TickFrame() override; void TickFrame() override;
bool AccelerateConditionalRendering() override;
bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src,
const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Surface& dst,
const Tegra::Engines::Fermi2D::Config& copy_config) override; const Tegra::Engines::Fermi2D::Config& copy_config) override;
@ -121,6 +127,9 @@ private:
static constexpr size_t MAX_IMAGES = 48; static constexpr size_t MAX_IMAGES = 48;
static constexpr size_t MAX_IMAGE_VIEWS = MAX_TEXTURES + MAX_IMAGES; static constexpr size_t MAX_IMAGE_VIEWS = MAX_TEXTURES + MAX_IMAGES;
template <typename Func>
void PrepareDraw(bool is_indexed, Func&&);
/// Syncs state to match guest's /// Syncs state to match guest's
void SyncState(); void SyncState();

View file

@ -51,7 +51,7 @@ using VideoCommon::LoadPipelines;
using VideoCommon::SerializePipeline; using VideoCommon::SerializePipeline;
using Context = ShaderContext::Context; using Context = ShaderContext::Context;
constexpr u32 CACHE_VERSION = 7; constexpr u32 CACHE_VERSION = 8;
template <typename Container> template <typename Container>
auto MakeSpan(Container& container) { auto MakeSpan(Container& container) {
@ -350,6 +350,7 @@ GraphicsPipeline* ShaderCache::CurrentGraphicsPipeline() {
regs.tessellation.params.output_primitives.Value() == regs.tessellation.params.output_primitives.Value() ==
Maxwell::Tessellation::OutputPrimitives::Triangles_CW); Maxwell::Tessellation::OutputPrimitives::Triangles_CW);
graphics_key.xfb_enabled.Assign(regs.transform_feedback_enabled != 0 ? 1 : 0); graphics_key.xfb_enabled.Assign(regs.transform_feedback_enabled != 0 ? 1 : 0);
graphics_key.app_stage.Assign(maxwell3d->engine_state);
if (graphics_key.xfb_enabled) { if (graphics_key.xfb_enabled) {
SetXfbState(graphics_key.xfb_state, regs); SetXfbState(graphics_key.xfb_state, regs);
} }

View file

@ -48,43 +48,30 @@ void RefreshXfbState(VideoCommon::TransformFeedbackState& state, const Maxwell&
} }
} // Anonymous namespace } // Anonymous namespace
void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFeatures& features) {
bool has_extended_dynamic_state, bool has_dynamic_vertex_input) {
const Maxwell& regs = maxwell3d.regs; const Maxwell& regs = maxwell3d.regs;
const auto topology_ = maxwell3d.draw_manager->GetDrawState().topology; const auto topology_ = maxwell3d.draw_manager->GetDrawState().topology;
const std::array enabled_lut{
regs.polygon_offset_point_enable,
regs.polygon_offset_line_enable,
regs.polygon_offset_fill_enable,
};
const u32 topology_index = static_cast<u32>(topology_);
raw1 = 0; raw1 = 0;
extended_dynamic_state.Assign(has_extended_dynamic_state ? 1 : 0); extended_dynamic_state.Assign(features.has_extended_dynamic_state ? 1 : 0);
dynamic_vertex_input.Assign(has_dynamic_vertex_input ? 1 : 0); extended_dynamic_state_2.Assign(features.has_extended_dynamic_state_2 ? 1 : 0);
extended_dynamic_state_2_extra.Assign(features.has_extended_dynamic_state_2_extra ? 1 : 0);
extended_dynamic_state_3_blend.Assign(features.has_extended_dynamic_state_3_blend ? 1 : 0);
extended_dynamic_state_3_enables.Assign(features.has_extended_dynamic_state_3_enables ? 1 : 0);
dynamic_vertex_input.Assign(features.has_dynamic_vertex_input ? 1 : 0);
xfb_enabled.Assign(regs.transform_feedback_enabled != 0); xfb_enabled.Assign(regs.transform_feedback_enabled != 0);
primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0);
depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0);
depth_clamp_disabled.Assign(regs.viewport_clip_control.geometry_clip ==
Maxwell::ViewportClipControl::GeometryClip::Passthrough ||
regs.viewport_clip_control.geometry_clip ==
Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ ||
regs.viewport_clip_control.geometry_clip ==
Maxwell::ViewportClipControl::GeometryClip::FrustumZ);
ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0); ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0);
polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front)); polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front));
patch_control_points_minus_one.Assign(regs.patch_vertices - 1);
tessellation_primitive.Assign(static_cast<u32>(regs.tessellation.params.domain_type.Value())); tessellation_primitive.Assign(static_cast<u32>(regs.tessellation.params.domain_type.Value()));
tessellation_spacing.Assign(static_cast<u32>(regs.tessellation.params.spacing.Value())); tessellation_spacing.Assign(static_cast<u32>(regs.tessellation.params.spacing.Value()));
tessellation_clockwise.Assign(regs.tessellation.params.output_primitives.Value() == tessellation_clockwise.Assign(regs.tessellation.params.output_primitives.Value() ==
Maxwell::Tessellation::OutputPrimitives::Triangles_CW); Maxwell::Tessellation::OutputPrimitives::Triangles_CW);
logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0); patch_control_points_minus_one.Assign(regs.patch_vertices - 1);
logic_op.Assign(PackLogicOp(regs.logic_op.op));
topology.Assign(topology_); topology.Assign(topology_);
msaa_mode.Assign(regs.anti_alias_samples_mode); msaa_mode.Assign(regs.anti_alias_samples_mode);
raw2 = 0; raw2 = 0;
rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0);
const auto test_func = const auto test_func =
regs.alpha_test_enabled != 0 ? regs.alpha_test_func : Maxwell::ComparisonOp::Always_GL; regs.alpha_test_enabled != 0 ? regs.alpha_test_func : Maxwell::ComparisonOp::Always_GL;
alpha_test_func.Assign(PackComparisonOp(test_func)); alpha_test_func.Assign(PackComparisonOp(test_func));
@ -97,6 +84,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d,
smooth_lines.Assign(regs.line_anti_alias_enable != 0 ? 1 : 0); smooth_lines.Assign(regs.line_anti_alias_enable != 0 ? 1 : 0);
alpha_to_coverage_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_coverage != 0 ? 1 : 0); alpha_to_coverage_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_coverage != 0 ? 1 : 0);
alpha_to_one_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_one != 0 ? 1 : 0); alpha_to_one_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_one != 0 ? 1 : 0);
app_stage.Assign(maxwell3d.engine_state);
for (size_t i = 0; i < regs.rt.size(); ++i) { for (size_t i = 0; i < regs.rt.size(); ++i) {
color_formats[i] = static_cast<u8>(regs.rt[i].format); color_formats[i] = static_cast<u8>(regs.rt[i].format);
@ -105,7 +93,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d,
point_size = Common::BitCast<u32>(regs.point_size); point_size = Common::BitCast<u32>(regs.point_size);
if (maxwell3d.dirty.flags[Dirty::VertexInput]) { if (maxwell3d.dirty.flags[Dirty::VertexInput]) {
if (has_dynamic_vertex_input) { if (features.has_dynamic_vertex_input) {
// Dirty flag will be reset by the command buffer update // Dirty flag will be reset by the command buffer update
static constexpr std::array LUT{ static constexpr std::array LUT{
0u, // Invalid 0u, // Invalid
@ -144,12 +132,6 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d,
} }
} }
} }
if (maxwell3d.dirty.flags[Dirty::Blending]) {
maxwell3d.dirty.flags[Dirty::Blending] = false;
for (size_t index = 0; index < attachments.size(); ++index) {
attachments[index].Refresh(regs, index);
}
}
if (maxwell3d.dirty.flags[Dirty::ViewportSwizzles]) { if (maxwell3d.dirty.flags[Dirty::ViewportSwizzles]) {
maxwell3d.dirty.flags[Dirty::ViewportSwizzles] = false; maxwell3d.dirty.flags[Dirty::ViewportSwizzles] = false;
const auto& transform = regs.viewport_transform; const auto& transform = regs.viewport_transform;
@ -157,8 +139,27 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d,
return static_cast<u16>(viewport.swizzle.raw); return static_cast<u16>(viewport.swizzle.raw);
}); });
} }
dynamic_state.raw1 = 0;
dynamic_state.raw2 = 0;
if (!extended_dynamic_state) { if (!extended_dynamic_state) {
dynamic_state.Refresh(regs); dynamic_state.Refresh(regs);
std::ranges::transform(regs.vertex_streams, vertex_strides.begin(), [](const auto& array) {
return static_cast<u16>(array.stride.Value());
});
}
if (!extended_dynamic_state_2_extra) {
dynamic_state.Refresh2(regs, topology, extended_dynamic_state_2);
}
if (!extended_dynamic_state_3_blend) {
if (maxwell3d.dirty.flags[Dirty::Blending]) {
maxwell3d.dirty.flags[Dirty::Blending] = false;
for (size_t index = 0; index < attachments.size(); ++index) {
attachments[index].Refresh(regs, index);
}
}
}
if (!extended_dynamic_state_3_enables) {
dynamic_state.Refresh3(regs);
} }
if (xfb_enabled) { if (xfb_enabled) {
RefreshXfbState(xfb_state, regs); RefreshXfbState(xfb_state, regs);
@ -175,12 +176,11 @@ void FixedPipelineState::BlendingAttachment::Refresh(const Maxwell& regs, size_t
mask_a.Assign(mask.A); mask_a.Assign(mask.A);
// TODO: C++20 Use templated lambda to deduplicate code // TODO: C++20 Use templated lambda to deduplicate code
if (!regs.blend.enable[index]) {
return;
}
if (!regs.blend_per_target_enabled) { const auto setup_blend = [&]<typename T>(const T& src) {
if (!regs.blend.enable[index]) {
return;
}
const auto& src = regs.blend;
equation_rgb.Assign(PackBlendEquation(src.color_op)); equation_rgb.Assign(PackBlendEquation(src.color_op));
equation_a.Assign(PackBlendEquation(src.alpha_op)); equation_a.Assign(PackBlendEquation(src.alpha_op));
factor_source_rgb.Assign(PackBlendFactor(src.color_source)); factor_source_rgb.Assign(PackBlendFactor(src.color_source));
@ -188,20 +188,13 @@ void FixedPipelineState::BlendingAttachment::Refresh(const Maxwell& regs, size_t
factor_source_a.Assign(PackBlendFactor(src.alpha_source)); factor_source_a.Assign(PackBlendFactor(src.alpha_source));
factor_dest_a.Assign(PackBlendFactor(src.alpha_dest)); factor_dest_a.Assign(PackBlendFactor(src.alpha_dest));
enable.Assign(1); enable.Assign(1);
return; };
}
if (!regs.blend.enable[index]) { if (!regs.blend_per_target_enabled) {
setup_blend(regs.blend);
return; return;
} }
const auto& src = regs.blend_per_target[index]; setup_blend(regs.blend_per_target[index]);
equation_rgb.Assign(PackBlendEquation(src.color_op));
equation_a.Assign(PackBlendEquation(src.alpha_op));
factor_source_rgb.Assign(PackBlendFactor(src.color_source));
factor_dest_rgb.Assign(PackBlendFactor(src.color_dest));
factor_source_a.Assign(PackBlendFactor(src.alpha_source));
factor_dest_a.Assign(PackBlendFactor(src.alpha_dest));
enable.Assign(1);
} }
void FixedPipelineState::DynamicState::Refresh(const Maxwell& regs) { void FixedPipelineState::DynamicState::Refresh(const Maxwell& regs) {
@ -211,8 +204,6 @@ void FixedPipelineState::DynamicState::Refresh(const Maxwell& regs) {
packed_front_face = 1 - packed_front_face; packed_front_face = 1 - packed_front_face;
} }
raw1 = 0;
raw2 = 0;
front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op.fail)); front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op.fail));
front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op.zfail)); front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op.zfail));
front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op.zpass)); front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op.zpass));
@ -236,9 +227,37 @@ void FixedPipelineState::DynamicState::Refresh(const Maxwell& regs) {
depth_test_func.Assign(PackComparisonOp(regs.depth_test_func)); depth_test_func.Assign(PackComparisonOp(regs.depth_test_func));
cull_face.Assign(PackCullFace(regs.gl_cull_face)); cull_face.Assign(PackCullFace(regs.gl_cull_face));
cull_enable.Assign(regs.gl_cull_test_enabled != 0 ? 1 : 0); cull_enable.Assign(regs.gl_cull_test_enabled != 0 ? 1 : 0);
std::ranges::transform(regs.vertex_streams, vertex_strides.begin(), [](const auto& array) { }
return static_cast<u16>(array.stride.Value());
}); void FixedPipelineState::DynamicState::Refresh2(const Maxwell& regs,
Maxwell::PrimitiveTopology topology_,
bool base_feautures_supported) {
logic_op.Assign(PackLogicOp(regs.logic_op.op));
if (base_feautures_supported) {
return;
}
const std::array enabled_lut{
regs.polygon_offset_point_enable,
regs.polygon_offset_line_enable,
regs.polygon_offset_fill_enable,
};
const u32 topology_index = static_cast<u32>(topology_);
rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0);
primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0);
depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0);
}
void FixedPipelineState::DynamicState::Refresh3(const Maxwell& regs) {
logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0);
depth_clamp_disabled.Assign(regs.viewport_clip_control.geometry_clip ==
Maxwell::ViewportClipControl::GeometryClip::Passthrough ||
regs.viewport_clip_control.geometry_clip ==
Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ ||
regs.viewport_clip_control.geometry_clip ==
Maxwell::ViewportClipControl::GeometryClip::FrustumZ);
} }
size_t FixedPipelineState::Hash() const noexcept { size_t FixedPipelineState::Hash() const noexcept {

View file

@ -17,6 +17,15 @@ namespace Vulkan {
using Maxwell = Tegra::Engines::Maxwell3D::Regs; using Maxwell = Tegra::Engines::Maxwell3D::Regs;
struct DynamicFeatures {
bool has_extended_dynamic_state;
bool has_extended_dynamic_state_2;
bool has_extended_dynamic_state_2_extra;
bool has_extended_dynamic_state_3_blend;
bool has_extended_dynamic_state_3_enables;
bool has_dynamic_vertex_input;
};
struct FixedPipelineState { struct FixedPipelineState {
static u32 PackComparisonOp(Maxwell::ComparisonOp op) noexcept; static u32 PackComparisonOp(Maxwell::ComparisonOp op) noexcept;
static Maxwell::ComparisonOp UnpackComparisonOp(u32 packed) noexcept; static Maxwell::ComparisonOp UnpackComparisonOp(u32 packed) noexcept;
@ -133,6 +142,17 @@ struct FixedPipelineState {
struct DynamicState { struct DynamicState {
union { union {
u32 raw1; u32 raw1;
BitField<0, 2, u32> cull_face;
BitField<2, 1, u32> cull_enable;
BitField<3, 1, u32> primitive_restart_enable;
BitField<4, 1, u32> depth_bias_enable;
BitField<5, 1, u32> rasterize_enable;
BitField<6, 4, u32> logic_op;
BitField<10, 1, u32> logic_op_enable;
BitField<11, 1, u32> depth_clamp_disabled;
};
union {
u32 raw2;
StencilFace<0> front; StencilFace<0> front;
StencilFace<12> back; StencilFace<12> back;
BitField<24, 1, u32> stencil_enable; BitField<24, 1, u32> stencil_enable;
@ -142,15 +162,11 @@ struct FixedPipelineState {
BitField<28, 1, u32> front_face; BitField<28, 1, u32> front_face;
BitField<29, 3, u32> depth_test_func; BitField<29, 3, u32> depth_test_func;
}; };
union {
u32 raw2;
BitField<0, 2, u32> cull_face;
BitField<2, 1, u32> cull_enable;
};
// Vertex stride is a 12 bits value, we have 4 bits to spare per element
std::array<u16, Maxwell::NumVertexArrays> vertex_strides;
void Refresh(const Maxwell& regs); void Refresh(const Maxwell& regs);
void Refresh2(const Maxwell& regs, Maxwell::PrimitiveTopology topology,
bool base_feautures_supported);
void Refresh3(const Maxwell& regs);
Maxwell::ComparisonOp DepthTestFunc() const noexcept { Maxwell::ComparisonOp DepthTestFunc() const noexcept {
return UnpackComparisonOp(depth_test_func); return UnpackComparisonOp(depth_test_func);
@ -168,25 +184,24 @@ struct FixedPipelineState {
union { union {
u32 raw1; u32 raw1;
BitField<0, 1, u32> extended_dynamic_state; BitField<0, 1, u32> extended_dynamic_state;
BitField<1, 1, u32> dynamic_vertex_input; BitField<1, 1, u32> extended_dynamic_state_2;
BitField<2, 1, u32> xfb_enabled; BitField<2, 1, u32> extended_dynamic_state_2_extra;
BitField<3, 1, u32> primitive_restart_enable; BitField<3, 1, u32> extended_dynamic_state_3_blend;
BitField<4, 1, u32> depth_bias_enable; BitField<4, 1, u32> extended_dynamic_state_3_enables;
BitField<5, 1, u32> depth_clamp_disabled; BitField<5, 1, u32> dynamic_vertex_input;
BitField<6, 1, u32> ndc_minus_one_to_one; BitField<6, 1, u32> xfb_enabled;
BitField<7, 2, u32> polygon_mode; BitField<7, 1, u32> ndc_minus_one_to_one;
BitField<9, 5, u32> patch_control_points_minus_one; BitField<8, 2, u32> polygon_mode;
BitField<14, 2, u32> tessellation_primitive; BitField<10, 2, u32> tessellation_primitive;
BitField<16, 2, u32> tessellation_spacing; BitField<12, 2, u32> tessellation_spacing;
BitField<18, 1, u32> tessellation_clockwise; BitField<14, 1, u32> tessellation_clockwise;
BitField<19, 1, u32> logic_op_enable; BitField<15, 5, u32> patch_control_points_minus_one;
BitField<20, 4, u32> logic_op;
BitField<24, 4, Maxwell::PrimitiveTopology> topology; BitField<24, 4, Maxwell::PrimitiveTopology> topology;
BitField<28, 4, Tegra::Texture::MsaaMode> msaa_mode; BitField<28, 4, Tegra::Texture::MsaaMode> msaa_mode;
}; };
union { union {
u32 raw2; u32 raw2;
BitField<0, 1, u32> rasterize_enable;
BitField<1, 3, u32> alpha_test_func; BitField<1, 3, u32> alpha_test_func;
BitField<4, 1, u32> early_z; BitField<4, 1, u32> early_z;
BitField<5, 1, u32> depth_enabled; BitField<5, 1, u32> depth_enabled;
@ -197,25 +212,28 @@ struct FixedPipelineState {
BitField<14, 1, u32> smooth_lines; BitField<14, 1, u32> smooth_lines;
BitField<15, 1, u32> alpha_to_coverage_enabled; BitField<15, 1, u32> alpha_to_coverage_enabled;
BitField<16, 1, u32> alpha_to_one_enabled; BitField<16, 1, u32> alpha_to_one_enabled;
BitField<17, 3, Tegra::Engines::Maxwell3D::EngineHint> app_stage;
}; };
std::array<u8, Maxwell::NumRenderTargets> color_formats; std::array<u8, Maxwell::NumRenderTargets> color_formats;
u32 alpha_test_ref; u32 alpha_test_ref;
u32 point_size; u32 point_size;
std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments;
std::array<u16, Maxwell::NumViewports> viewport_swizzles; std::array<u16, Maxwell::NumViewports> viewport_swizzles;
union { union {
u64 attribute_types; // Used with VK_EXT_vertex_input_dynamic_state u64 attribute_types; // Used with VK_EXT_vertex_input_dynamic_state
u64 enabled_divisors; u64 enabled_divisors;
}; };
std::array<VertexAttribute, Maxwell::NumVertexAttributes> attributes;
std::array<u32, Maxwell::NumVertexArrays> binding_divisors;
DynamicState dynamic_state; DynamicState dynamic_state;
std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments;
std::array<VertexAttribute, Maxwell::NumVertexAttributes> attributes;
std::array<u32, Maxwell::NumVertexArrays> binding_divisors;
// Vertex stride is a 12 bits value, we have 4 bits to spare per element
std::array<u16, Maxwell::NumVertexArrays> vertex_strides;
VideoCommon::TransformFeedbackState xfb_state; VideoCommon::TransformFeedbackState xfb_state;
void Refresh(Tegra::Engines::Maxwell3D& maxwell3d, bool has_extended_dynamic_state, void Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFeatures& features);
bool has_dynamic_vertex_input);
size_t Hash() const noexcept; size_t Hash() const noexcept;
@ -230,13 +248,17 @@ struct FixedPipelineState {
// When transform feedback is enabled, use the whole struct // When transform feedback is enabled, use the whole struct
return sizeof(*this); return sizeof(*this);
} }
if (dynamic_vertex_input) { if (dynamic_vertex_input && extended_dynamic_state_3_blend) {
// Exclude dynamic state and attributes // Exclude dynamic state and attributes
return offsetof(FixedPipelineState, dynamic_state);
}
if (dynamic_vertex_input) {
// Exclude dynamic state
return offsetof(FixedPipelineState, attributes); return offsetof(FixedPipelineState, attributes);
} }
if (extended_dynamic_state) { if (extended_dynamic_state) {
// Exclude dynamic state // Exclude dynamic state
return offsetof(FixedPipelineState, dynamic_state); return offsetof(FixedPipelineState, vertex_strides);
} }
// Default // Default
return offsetof(FixedPipelineState, xfb_state); return offsetof(FixedPipelineState, xfb_state);

View file

@ -56,7 +56,8 @@ vk::Buffer CreateBuffer(const Device& device, u64 size) {
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT |
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
if (device.IsExtTransformFeedbackSupported()) { if (device.IsExtTransformFeedbackSupported()) {
flags |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT; flags |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
} }
@ -516,6 +517,7 @@ void BufferCacheRuntime::ReserveNullBuffer() {
if (device.IsExtTransformFeedbackSupported()) { if (device.IsExtTransformFeedbackSupported()) {
create_info.usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT; create_info.usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
} }
create_info.usage |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
null_buffer = device.GetLogical().CreateBuffer(create_info); null_buffer = device.GetLogical().CreateBuffer(create_info);
if (device.HasDebuggingToolAttached()) { if (device.HasDebuggingToolAttached()) {
null_buffer.SetObjectNameEXT("Null buffer"); null_buffer.SetObjectNameEXT("Null buffer");

View file

@ -201,6 +201,22 @@ struct SimpleVertexSpec {
static constexpr bool has_images = false; static constexpr bool has_images = false;
}; };
struct SimpleStorageSpec {
static constexpr std::array<bool, 5> enabled_stages{true, false, false, false, true};
static constexpr bool has_storage_buffers = true;
static constexpr bool has_texture_buffers = false;
static constexpr bool has_image_buffers = false;
static constexpr bool has_images = false;
};
struct SimpleImageSpec {
static constexpr std::array<bool, 5> enabled_stages{true, false, false, false, true};
static constexpr bool has_storage_buffers = false;
static constexpr bool has_texture_buffers = false;
static constexpr bool has_image_buffers = false;
static constexpr bool has_images = true;
};
struct DefaultSpec { struct DefaultSpec {
static constexpr std::array<bool, 5> enabled_stages{true, true, true, true, true}; static constexpr std::array<bool, 5> enabled_stages{true, true, true, true, true};
static constexpr bool has_storage_buffers = true; static constexpr bool has_storage_buffers = true;
@ -211,7 +227,8 @@ struct DefaultSpec {
ConfigureFuncPtr ConfigureFunc(const std::array<vk::ShaderModule, NUM_STAGES>& modules, ConfigureFuncPtr ConfigureFunc(const std::array<vk::ShaderModule, NUM_STAGES>& modules,
const std::array<Shader::Info, NUM_STAGES>& infos) { const std::array<Shader::Info, NUM_STAGES>& infos) {
return FindSpec<SimpleVertexSpec, SimpleVertexFragmentSpec, DefaultSpec>(modules, infos); return FindSpec<SimpleVertexSpec, SimpleVertexFragmentSpec, SimpleStorageSpec, SimpleImageSpec,
DefaultSpec>(modules, infos);
} }
} // Anonymous namespace } // Anonymous namespace
@ -524,6 +541,8 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
FixedPipelineState::DynamicState dynamic{}; FixedPipelineState::DynamicState dynamic{};
if (!key.state.extended_dynamic_state) { if (!key.state.extended_dynamic_state) {
dynamic = key.state.dynamic_state; dynamic = key.state.dynamic_state;
} else {
dynamic.raw1 = key.state.dynamic_state.raw1;
} }
static_vector<VkVertexInputBindingDescription, 32> vertex_bindings; static_vector<VkVertexInputBindingDescription, 32> vertex_bindings;
static_vector<VkVertexInputBindingDivisorDescriptionEXT, 32> vertex_binding_divisors; static_vector<VkVertexInputBindingDivisorDescriptionEXT, 32> vertex_binding_divisors;
@ -561,7 +580,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;
vertex_bindings.push_back({ vertex_bindings.push_back({
.binding = static_cast<u32>(index), .binding = static_cast<u32>(index),
.stride = dynamic.vertex_strides[index], .stride = key.state.vertex_strides[index],
.inputRate = rate, .inputRate = rate,
}); });
if (instanced) { if (instanced) {
@ -625,7 +644,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
.pNext = nullptr, .pNext = nullptr,
.flags = 0, .flags = 0,
.topology = input_assembly_topology, .topology = input_assembly_topology,
.primitiveRestartEnable = key.state.primitive_restart_enable != 0 && .primitiveRestartEnable = dynamic.primitive_restart_enable != 0 &&
((input_assembly_topology != VK_PRIMITIVE_TOPOLOGY_PATCH_LIST && ((input_assembly_topology != VK_PRIMITIVE_TOPOLOGY_PATCH_LIST &&
device.IsTopologyListPrimitiveRestartSupported()) || device.IsTopologyListPrimitiveRestartSupported()) ||
SupportsPrimitiveRestart(input_assembly_topology) || SupportsPrimitiveRestart(input_assembly_topology) ||
@ -672,15 +691,15 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
.pNext = nullptr, .pNext = nullptr,
.flags = 0, .flags = 0,
.depthClampEnable = .depthClampEnable =
static_cast<VkBool32>(key.state.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE), static_cast<VkBool32>(dynamic.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE),
.rasterizerDiscardEnable = .rasterizerDiscardEnable =
static_cast<VkBool32>(key.state.rasterize_enable == 0 ? VK_TRUE : VK_FALSE), static_cast<VkBool32>(dynamic.rasterize_enable == 0 ? VK_TRUE : VK_FALSE),
.polygonMode = .polygonMode =
MaxwellToVK::PolygonMode(FixedPipelineState::UnpackPolygonMode(key.state.polygon_mode)), MaxwellToVK::PolygonMode(FixedPipelineState::UnpackPolygonMode(key.state.polygon_mode)),
.cullMode = static_cast<VkCullModeFlags>( .cullMode = static_cast<VkCullModeFlags>(
dynamic.cull_enable ? MaxwellToVK::CullFace(dynamic.CullFace()) : VK_CULL_MODE_NONE), dynamic.cull_enable ? MaxwellToVK::CullFace(dynamic.CullFace()) : VK_CULL_MODE_NONE),
.frontFace = MaxwellToVK::FrontFace(dynamic.FrontFace()), .frontFace = MaxwellToVK::FrontFace(dynamic.FrontFace()),
.depthBiasEnable = key.state.depth_bias_enable, .depthBiasEnable = (dynamic.depth_bias_enable == 0 ? VK_TRUE : VK_FALSE),
.depthBiasConstantFactor = 0.0f, .depthBiasConstantFactor = 0.0f,
.depthBiasClamp = 0.0f, .depthBiasClamp = 0.0f,
.depthBiasSlopeFactor = 0.0f, .depthBiasSlopeFactor = 0.0f,
@ -782,13 +801,13 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.pNext = nullptr, .pNext = nullptr,
.flags = 0, .flags = 0,
.logicOpEnable = key.state.logic_op_enable != 0, .logicOpEnable = dynamic.logic_op_enable != 0,
.logicOp = static_cast<VkLogicOp>(key.state.logic_op.Value()), .logicOp = static_cast<VkLogicOp>(dynamic.logic_op.Value()),
.attachmentCount = static_cast<u32>(cb_attachments.size()), .attachmentCount = static_cast<u32>(cb_attachments.size()),
.pAttachments = cb_attachments.data(), .pAttachments = cb_attachments.data(),
.blendConstants = {}, .blendConstants = {},
}; };
static_vector<VkDynamicState, 19> dynamic_states{ static_vector<VkDynamicState, 28> dynamic_states{
VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR,
VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS, VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS,
VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK,
@ -811,6 +830,32 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT); dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT);
} }
dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end()); dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end());
if (key.state.extended_dynamic_state_2) {
static constexpr std::array extended2{
VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE_EXT,
VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE_EXT,
VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE_EXT,
};
dynamic_states.insert(dynamic_states.end(), extended2.begin(), extended2.end());
}
if (key.state.extended_dynamic_state_2_extra) {
dynamic_states.push_back(VK_DYNAMIC_STATE_LOGIC_OP_EXT);
}
if (key.state.extended_dynamic_state_3_blend) {
static constexpr std::array extended3{
VK_DYNAMIC_STATE_COLOR_BLEND_ENABLE_EXT,
VK_DYNAMIC_STATE_COLOR_BLEND_EQUATION_EXT,
VK_DYNAMIC_STATE_COLOR_WRITE_MASK_EXT,
};
dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end());
}
if (key.state.extended_dynamic_state_3_enables) {
static constexpr std::array extended3{
VK_DYNAMIC_STATE_DEPTH_CLAMP_ENABLE_EXT,
VK_DYNAMIC_STATE_LOGIC_OP_ENABLE_EXT,
};
dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end());
}
} }
const VkPipelineDynamicStateCreateInfo dynamic_state_ci{ const VkPipelineDynamicStateCreateInfo dynamic_state_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,

View file

@ -54,7 +54,7 @@ using VideoCommon::FileEnvironment;
using VideoCommon::GenericEnvironment; using VideoCommon::GenericEnvironment;
using VideoCommon::GraphicsEnvironment; using VideoCommon::GraphicsEnvironment;
constexpr u32 CACHE_VERSION = 8; constexpr u32 CACHE_VERSION = 9;
template <typename Container> template <typename Container>
auto MakeSpan(Container& container) { auto MakeSpan(Container& container) {
@ -351,6 +351,15 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
LOG_WARNING(Render_Vulkan, "maxVertexInputBindings is too low: {} < {}", LOG_WARNING(Render_Vulkan, "maxVertexInputBindings is too low: {} < {}",
device.GetMaxVertexInputBindings(), Maxwell::NumVertexArrays); device.GetMaxVertexInputBindings(), Maxwell::NumVertexArrays);
} }
dynamic_features = DynamicFeatures{
.has_extended_dynamic_state = device.IsExtExtendedDynamicStateSupported(),
.has_extended_dynamic_state_2 = device.IsExtExtendedDynamicState2Supported(),
.has_extended_dynamic_state_2_extra = device.IsExtExtendedDynamicState2ExtrasSupported(),
.has_extended_dynamic_state_3_blend = device.IsExtExtendedDynamicState3BlendingSupported(),
.has_extended_dynamic_state_3_enables = device.IsExtExtendedDynamicState3EnablesSupported(),
.has_dynamic_vertex_input = device.IsExtVertexInputDynamicStateSupported(),
};
} }
PipelineCache::~PipelineCache() = default; PipelineCache::~PipelineCache() = default;
@ -362,8 +371,7 @@ GraphicsPipeline* PipelineCache::CurrentGraphicsPipeline() {
current_pipeline = nullptr; current_pipeline = nullptr;
return nullptr; return nullptr;
} }
graphics_key.state.Refresh(*maxwell3d, device.IsExtExtendedDynamicStateSupported(), graphics_key.state.Refresh(*maxwell3d, dynamic_features);
device.IsExtVertexInputDynamicStateSupported());
if (current_pipeline) { if (current_pipeline) {
GraphicsPipeline* const next{current_pipeline->Next(graphics_key)}; GraphicsPipeline* const next{current_pipeline->Next(graphics_key)};
@ -439,14 +447,21 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
}); });
++state.total; ++state.total;
}}; }};
const bool extended_dynamic_state = device.IsExtExtendedDynamicStateSupported();
const bool dynamic_vertex_input = device.IsExtVertexInputDynamicStateSupported();
const auto load_graphics{[&](std::ifstream& file, std::vector<FileEnvironment> envs) { const auto load_graphics{[&](std::ifstream& file, std::vector<FileEnvironment> envs) {
GraphicsPipelineCacheKey key; GraphicsPipelineCacheKey key;
file.read(reinterpret_cast<char*>(&key), sizeof(key)); file.read(reinterpret_cast<char*>(&key), sizeof(key));
if ((key.state.extended_dynamic_state != 0) != extended_dynamic_state || if ((key.state.extended_dynamic_state != 0) !=
(key.state.dynamic_vertex_input != 0) != dynamic_vertex_input) { dynamic_features.has_extended_dynamic_state ||
(key.state.extended_dynamic_state_2 != 0) !=
dynamic_features.has_extended_dynamic_state_2 ||
(key.state.extended_dynamic_state_2_extra != 0) !=
dynamic_features.has_extended_dynamic_state_2_extra ||
(key.state.extended_dynamic_state_3_blend != 0) !=
dynamic_features.has_extended_dynamic_state_3_blend ||
(key.state.extended_dynamic_state_3_enables != 0) !=
dynamic_features.has_extended_dynamic_state_3_enables ||
(key.state.dynamic_vertex_input != 0) != dynamic_features.has_dynamic_vertex_input) {
return; return;
} }
workers.QueueWork([this, key, envs = std::move(envs), &state, &callback]() mutable { workers.QueueWork([this, key, envs = std::move(envs), &state, &callback]() mutable {

View file

@ -160,6 +160,7 @@ private:
Common::ThreadWorker workers; Common::ThreadWorker workers;
Common::ThreadWorker serialization_thread; Common::ThreadWorker serialization_thread;
DynamicFeatures dynamic_features;
}; };
} // namespace Vulkan } // namespace Vulkan

View file

@ -180,7 +180,8 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra
RasterizerVulkan::~RasterizerVulkan() = default; RasterizerVulkan::~RasterizerVulkan() = default;
void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) { template <typename Func>
void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
MICROPROFILE_SCOPE(Vulkan_Drawing); MICROPROFILE_SCOPE(Vulkan_Drawing);
SCOPE_EXIT({ gpu.TickWork(); }); SCOPE_EXIT({ gpu.TickWork(); });
@ -201,22 +202,69 @@ void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
UpdateDynamicStates(); UpdateDynamicStates();
const auto& draw_state = maxwell3d->draw_manager->GetDrawState(); draw_func();
const u32 num_instances{instance_count};
const DrawParams draw_params{MakeDrawParams(draw_state, num_instances, is_indexed)};
scheduler.Record([draw_params](vk::CommandBuffer cmdbuf) {
if (draw_params.is_indexed) {
cmdbuf.DrawIndexed(draw_params.num_vertices, draw_params.num_instances,
draw_params.first_index, draw_params.base_vertex,
draw_params.base_instance);
} else {
cmdbuf.Draw(draw_params.num_vertices, draw_params.num_instances,
draw_params.base_vertex, draw_params.base_instance);
}
});
EndTransformFeedback(); EndTransformFeedback();
} }
void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
PrepareDraw(is_indexed, [this, is_indexed, instance_count] {
const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
const u32 num_instances{instance_count};
const DrawParams draw_params{MakeDrawParams(draw_state, num_instances, is_indexed)};
scheduler.Record([draw_params](vk::CommandBuffer cmdbuf) {
if (draw_params.is_indexed) {
cmdbuf.DrawIndexed(draw_params.num_vertices, draw_params.num_instances,
draw_params.first_index, draw_params.base_vertex,
draw_params.base_instance);
} else {
cmdbuf.Draw(draw_params.num_vertices, draw_params.num_instances,
draw_params.base_vertex, draw_params.base_instance);
}
});
});
}
void RasterizerVulkan::DrawIndirect() {
const auto& params = maxwell3d->draw_manager->GetIndirectParams();
buffer_cache.SetDrawIndirect(&params);
PrepareDraw(params.is_indexed, [this, &params] {
const auto indirect_buffer = buffer_cache.GetDrawIndirectBuffer();
const auto& buffer = indirect_buffer.first;
const auto& offset = indirect_buffer.second;
if (params.include_count) {
const auto count = buffer_cache.GetDrawIndirectCount();
const auto& draw_buffer = count.first;
const auto& offset_base = count.second;
scheduler.Record([draw_buffer_obj = draw_buffer->Handle(),
buffer_obj = buffer->Handle(), offset_base, offset,
params](vk::CommandBuffer cmdbuf) {
if (params.is_indexed) {
cmdbuf.DrawIndexedIndirectCount(
buffer_obj, offset, draw_buffer_obj, offset_base,
static_cast<u32>(params.max_draw_counts), static_cast<u32>(params.stride));
} else {
cmdbuf.DrawIndirectCount(buffer_obj, offset, draw_buffer_obj, offset_base,
static_cast<u32>(params.max_draw_counts),
static_cast<u32>(params.stride));
}
});
return;
}
scheduler.Record([buffer_obj = buffer->Handle(), offset, params](vk::CommandBuffer cmdbuf) {
if (params.is_indexed) {
cmdbuf.DrawIndexedIndirect(buffer_obj, offset,
static_cast<u32>(params.max_draw_counts),
static_cast<u32>(params.stride));
} else {
cmdbuf.DrawIndirect(buffer_obj, offset, static_cast<u32>(params.max_draw_counts),
static_cast<u32>(params.stride));
}
});
});
buffer_cache.SetDrawIndirect(nullptr);
}
void RasterizerVulkan::Clear(u32 layer_count) { void RasterizerVulkan::Clear(u32 layer_count) {
MICROPROFILE_SCOPE(Vulkan_Clearing); MICROPROFILE_SCOPE(Vulkan_Clearing);
@ -379,44 +427,58 @@ void Vulkan::RasterizerVulkan::DisableGraphicsUniformBuffer(size_t stage, u32 in
void RasterizerVulkan::FlushAll() {} void RasterizerVulkan::FlushAll() {}
void RasterizerVulkan::FlushRegion(VAddr addr, u64 size) { void RasterizerVulkan::FlushRegion(VAddr addr, u64 size, VideoCommon::CacheType which) {
if (addr == 0 || size == 0) { if (addr == 0 || size == 0) {
return; return;
} }
{ if (bool(which & VideoCommon::CacheType::TextureCache)) {
std::scoped_lock lock{texture_cache.mutex}; std::scoped_lock lock{texture_cache.mutex};
texture_cache.DownloadMemory(addr, size); texture_cache.DownloadMemory(addr, size);
} }
{ if ((bool(which & VideoCommon::CacheType::BufferCache))) {
std::scoped_lock lock{buffer_cache.mutex}; std::scoped_lock lock{buffer_cache.mutex};
buffer_cache.DownloadMemory(addr, size); buffer_cache.DownloadMemory(addr, size);
} }
query_cache.FlushRegion(addr, size); if ((bool(which & VideoCommon::CacheType::QueryCache))) {
} query_cache.FlushRegion(addr, size);
bool RasterizerVulkan::MustFlushRegion(VAddr addr, u64 size) {
std::scoped_lock lock{texture_cache.mutex, buffer_cache.mutex};
if (!Settings::IsGPULevelHigh()) {
return buffer_cache.IsRegionGpuModified(addr, size);
} }
return texture_cache.IsRegionGpuModified(addr, size) ||
buffer_cache.IsRegionGpuModified(addr, size);
} }
void RasterizerVulkan::InvalidateRegion(VAddr addr, u64 size) { bool RasterizerVulkan::MustFlushRegion(VAddr addr, u64 size, VideoCommon::CacheType which) {
if ((bool(which & VideoCommon::CacheType::BufferCache))) {
std::scoped_lock lock{buffer_cache.mutex};
if (buffer_cache.IsRegionGpuModified(addr, size)) {
return true;
}
}
if (!Settings::IsGPULevelHigh()) {
return false;
}
if (bool(which & VideoCommon::CacheType::TextureCache)) {
std::scoped_lock lock{texture_cache.mutex};
return texture_cache.IsRegionGpuModified(addr, size);
}
return false;
}
void RasterizerVulkan::InvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType which) {
if (addr == 0 || size == 0) { if (addr == 0 || size == 0) {
return; return;
} }
{ if (bool(which & VideoCommon::CacheType::TextureCache)) {
std::scoped_lock lock{texture_cache.mutex}; std::scoped_lock lock{texture_cache.mutex};
texture_cache.WriteMemory(addr, size); texture_cache.WriteMemory(addr, size);
} }
{ if ((bool(which & VideoCommon::CacheType::BufferCache))) {
std::scoped_lock lock{buffer_cache.mutex}; std::scoped_lock lock{buffer_cache.mutex};
buffer_cache.WriteMemory(addr, size); buffer_cache.WriteMemory(addr, size);
} }
pipeline_cache.InvalidateRegion(addr, size); if ((bool(which & VideoCommon::CacheType::QueryCache))) {
query_cache.InvalidateRegion(addr, size); query_cache.InvalidateRegion(addr, size);
}
if ((bool(which & VideoCommon::CacheType::ShaderCache))) {
pipeline_cache.InvalidateRegion(addr, size);
}
} }
void RasterizerVulkan::OnCPUWrite(VAddr addr, u64 size) { void RasterizerVulkan::OnCPUWrite(VAddr addr, u64 size) {
@ -481,11 +543,12 @@ void RasterizerVulkan::ReleaseFences() {
fence_manager.WaitPendingFences(); fence_manager.WaitPendingFences();
} }
void RasterizerVulkan::FlushAndInvalidateRegion(VAddr addr, u64 size) { void RasterizerVulkan::FlushAndInvalidateRegion(VAddr addr, u64 size,
VideoCommon::CacheType which) {
if (Settings::IsGPULevelExtreme()) { if (Settings::IsGPULevelExtreme()) {
FlushRegion(addr, size); FlushRegion(addr, size, which);
} }
InvalidateRegion(addr, size); InvalidateRegion(addr, size, which);
} }
void RasterizerVulkan::WaitForIdle() { void RasterizerVulkan::WaitForIdle() {
@ -541,6 +604,21 @@ void RasterizerVulkan::TickFrame() {
} }
} }
bool RasterizerVulkan::AccelerateConditionalRendering() {
if (Settings::IsGPULevelHigh()) {
// TODO(Blinkhawk): Reimplement Host conditional rendering.
return false;
}
// Medium / Low Hack: stub any checks on queries writen into the buffer cache.
const GPUVAddr condition_address{maxwell3d->regs.render_enable.Address()};
Maxwell::ReportSemaphore::Compare cmp;
if (gpu_memory->IsMemoryDirty(condition_address, sizeof(cmp),
VideoCommon::CacheType::BufferCache)) {
return true;
}
return false;
}
bool RasterizerVulkan::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, bool RasterizerVulkan::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src,
const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Surface& dst,
const Tegra::Engines::Fermi2D::Config& copy_config) { const Tegra::Engines::Fermi2D::Config& copy_config) {
@ -561,7 +639,7 @@ void RasterizerVulkan::AccelerateInlineToMemory(GPUVAddr address, size_t copy_si
} }
gpu_memory->WriteBlockUnsafe(address, memory.data(), copy_size); gpu_memory->WriteBlockUnsafe(address, memory.data(), copy_size);
{ {
std::unique_lock<std::mutex> lock{buffer_cache.mutex}; std::unique_lock<std::recursive_mutex> lock{buffer_cache.mutex};
if (!buffer_cache.InlineMemory(*cpu_addr, copy_size, memory)) { if (!buffer_cache.InlineMemory(*cpu_addr, copy_size, memory)) {
buffer_cache.WriteMemory(*cpu_addr, copy_size); buffer_cache.WriteMemory(*cpu_addr, copy_size);
} }
@ -639,16 +717,35 @@ void RasterizerVulkan::UpdateDynamicStates() {
UpdateLineWidth(regs); UpdateLineWidth(regs);
if (device.IsExtExtendedDynamicStateSupported()) { if (device.IsExtExtendedDynamicStateSupported()) {
UpdateCullMode(regs); UpdateCullMode(regs);
UpdateDepthBoundsTestEnable(regs);
UpdateDepthTestEnable(regs);
UpdateDepthWriteEnable(regs);
UpdateDepthCompareOp(regs); UpdateDepthCompareOp(regs);
UpdateFrontFace(regs); UpdateFrontFace(regs);
UpdateStencilOp(regs); UpdateStencilOp(regs);
UpdateStencilTestEnable(regs);
if (device.IsExtVertexInputDynamicStateSupported()) { if (device.IsExtVertexInputDynamicStateSupported()) {
UpdateVertexInput(regs); UpdateVertexInput(regs);
} }
if (state_tracker.TouchStateEnable()) {
UpdateDepthBoundsTestEnable(regs);
UpdateDepthTestEnable(regs);
UpdateDepthWriteEnable(regs);
UpdateStencilTestEnable(regs);
if (device.IsExtExtendedDynamicState2Supported()) {
UpdatePrimitiveRestartEnable(regs);
UpdateRasterizerDiscardEnable(regs);
UpdateDepthBiasEnable(regs);
}
if (device.IsExtExtendedDynamicState3EnablesSupported()) {
UpdateLogicOpEnable(regs);
UpdateDepthClampEnable(regs);
}
}
if (device.IsExtExtendedDynamicState2ExtrasSupported()) {
UpdateLogicOp(regs);
}
if (device.IsExtExtendedDynamicState3Supported()) {
UpdateBlending(regs);
}
} }
} }
@ -868,6 +965,82 @@ void RasterizerVulkan::UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& r
}); });
} }
void RasterizerVulkan::UpdatePrimitiveRestartEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
if (!state_tracker.TouchPrimitiveRestartEnable()) {
return;
}
scheduler.Record([enable = regs.primitive_restart.enabled](vk::CommandBuffer cmdbuf) {
cmdbuf.SetPrimitiveRestartEnableEXT(enable);
});
}
void RasterizerVulkan::UpdateRasterizerDiscardEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
if (!state_tracker.TouchRasterizerDiscardEnable()) {
return;
}
scheduler.Record([disable = regs.rasterize_enable](vk::CommandBuffer cmdbuf) {
cmdbuf.SetRasterizerDiscardEnableEXT(disable == 0);
});
}
void RasterizerVulkan::UpdateDepthBiasEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
if (!state_tracker.TouchDepthBiasEnable()) {
return;
}
constexpr size_t POINT = 0;
constexpr size_t LINE = 1;
constexpr size_t POLYGON = 2;
constexpr std::array POLYGON_OFFSET_ENABLE_LUT = {
POINT, // Points
LINE, // Lines
LINE, // LineLoop
LINE, // LineStrip
POLYGON, // Triangles
POLYGON, // TriangleStrip
POLYGON, // TriangleFan
POLYGON, // Quads
POLYGON, // QuadStrip
POLYGON, // Polygon
LINE, // LinesAdjacency
LINE, // LineStripAdjacency
POLYGON, // TrianglesAdjacency
POLYGON, // TriangleStripAdjacency
POLYGON, // Patches
};
const std::array enabled_lut{
regs.polygon_offset_point_enable,
regs.polygon_offset_line_enable,
regs.polygon_offset_fill_enable,
};
const u32 topology_index = static_cast<u32>(maxwell3d->draw_manager->GetDrawState().topology);
const u32 enable = enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]];
scheduler.Record(
[enable](vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthBiasEnableEXT(enable != 0); });
}
void RasterizerVulkan::UpdateLogicOpEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
if (!state_tracker.TouchLogicOpEnable()) {
return;
}
scheduler.Record([enable = regs.logic_op.enable](vk::CommandBuffer cmdbuf) {
cmdbuf.SetLogicOpEnableEXT(enable != 0);
});
}
void RasterizerVulkan::UpdateDepthClampEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
if (!state_tracker.TouchDepthClampEnable()) {
return;
}
bool is_enabled = !(regs.viewport_clip_control.geometry_clip ==
Maxwell::ViewportClipControl::GeometryClip::Passthrough ||
regs.viewport_clip_control.geometry_clip ==
Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ ||
regs.viewport_clip_control.geometry_clip ==
Maxwell::ViewportClipControl::GeometryClip::FrustumZ);
scheduler.Record(
[is_enabled](vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthClampEnableEXT(is_enabled); });
}
void RasterizerVulkan::UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs) { void RasterizerVulkan::UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs) {
if (!state_tracker.TouchDepthCompareOp()) { if (!state_tracker.TouchDepthCompareOp()) {
return; return;
@ -925,6 +1098,79 @@ void RasterizerVulkan::UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs) {
} }
} }
void RasterizerVulkan::UpdateLogicOp(Tegra::Engines::Maxwell3D::Regs& regs) {
if (!regs.logic_op.enable) {
return;
}
if (!state_tracker.TouchLogicOp()) {
return;
}
auto op = static_cast<VkLogicOp>(static_cast<u32>(regs.logic_op.op) - 0x1500);
scheduler.Record([op](vk::CommandBuffer cmdbuf) { cmdbuf.SetLogicOpEXT(op); });
}
void RasterizerVulkan::UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs) {
if (!state_tracker.TouchBlending()) {
return;
}
if (state_tracker.TouchColorMask()) {
std::array<VkColorComponentFlags, Maxwell::NumRenderTargets> setup_masks{};
for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) {
const auto& mask = regs.color_mask[regs.color_mask_common ? 0 : index];
auto& current = setup_masks[index];
if (mask.R) {
current |= VK_COLOR_COMPONENT_R_BIT;
}
if (mask.G) {
current |= VK_COLOR_COMPONENT_G_BIT;
}
if (mask.B) {
current |= VK_COLOR_COMPONENT_B_BIT;
}
if (mask.A) {
current |= VK_COLOR_COMPONENT_A_BIT;
}
}
scheduler.Record([setup_masks](vk::CommandBuffer cmdbuf) {
cmdbuf.SetColorWriteMaskEXT(0, setup_masks);
});
}
if (state_tracker.TouchBlendEnable()) {
std::array<VkBool32, Maxwell::NumRenderTargets> setup_enables{};
std::ranges::transform(
regs.blend.enable, setup_enables.begin(),
[&](const auto& is_enabled) { return is_enabled != 0 ? VK_TRUE : VK_FALSE; });
scheduler.Record([setup_enables](vk::CommandBuffer cmdbuf) {
cmdbuf.SetColorBlendEnableEXT(0, setup_enables);
});
}
if (state_tracker.TouchBlendEquations()) {
std::array<VkColorBlendEquationEXT, Maxwell::NumRenderTargets> setup_blends{};
for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) {
const auto blend_setup = [&]<typename T>(const T& guest_blend) {
auto& host_blend = setup_blends[index];
host_blend.srcColorBlendFactor = MaxwellToVK::BlendFactor(guest_blend.color_source);
host_blend.dstColorBlendFactor = MaxwellToVK::BlendFactor(guest_blend.color_dest);
host_blend.colorBlendOp = MaxwellToVK::BlendEquation(guest_blend.color_op);
host_blend.srcAlphaBlendFactor = MaxwellToVK::BlendFactor(guest_blend.alpha_source);
host_blend.dstAlphaBlendFactor = MaxwellToVK::BlendFactor(guest_blend.alpha_dest);
host_blend.alphaBlendOp = MaxwellToVK::BlendEquation(guest_blend.alpha_op);
};
if (!regs.blend_per_target_enabled) {
blend_setup(regs.blend);
continue;
}
blend_setup(regs.blend_per_target[index]);
}
scheduler.Record([setup_blends](vk::CommandBuffer cmdbuf) {
cmdbuf.SetColorBlendEquationEXT(0, setup_blends);
});
}
}
void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) { void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
if (!state_tracker.TouchStencilTestEnable()) { if (!state_tracker.TouchStencilTestEnable()) {
return; return;

View file

@ -65,6 +65,7 @@ public:
~RasterizerVulkan() override; ~RasterizerVulkan() override;
void Draw(bool is_indexed, u32 instance_count) override; void Draw(bool is_indexed, u32 instance_count) override;
void DrawIndirect() override;
void Clear(u32 layer_count) override; void Clear(u32 layer_count) override;
void DispatchCompute() override; void DispatchCompute() override;
void ResetCounter(VideoCore::QueryType type) override; void ResetCounter(VideoCore::QueryType type) override;
@ -72,9 +73,12 @@ public:
void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override; void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override;
void DisableGraphicsUniformBuffer(size_t stage, u32 index) override; void DisableGraphicsUniformBuffer(size_t stage, u32 index) override;
void FlushAll() override; void FlushAll() override;
void FlushRegion(VAddr addr, u64 size) override; void FlushRegion(VAddr addr, u64 size,
bool MustFlushRegion(VAddr addr, u64 size) override; VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
void InvalidateRegion(VAddr addr, u64 size) override; bool MustFlushRegion(VAddr addr, u64 size,
VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
void InvalidateRegion(VAddr addr, u64 size,
VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
void OnCPUWrite(VAddr addr, u64 size) override; void OnCPUWrite(VAddr addr, u64 size) override;
void InvalidateGPUCache() override; void InvalidateGPUCache() override;
void UnmapMemory(VAddr addr, u64 size) override; void UnmapMemory(VAddr addr, u64 size) override;
@ -84,12 +88,14 @@ public:
void SignalSyncPoint(u32 value) override; void SignalSyncPoint(u32 value) override;
void SignalReference() override; void SignalReference() override;
void ReleaseFences() override; void ReleaseFences() override;
void FlushAndInvalidateRegion(VAddr addr, u64 size) override; void FlushAndInvalidateRegion(
VAddr addr, u64 size, VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
void WaitForIdle() override; void WaitForIdle() override;
void FragmentBarrier() override; void FragmentBarrier() override;
void TiledCacheBarrier() override; void TiledCacheBarrier() override;
void FlushCommands() override; void FlushCommands() override;
void TickFrame() override; void TickFrame() override;
bool AccelerateConditionalRendering() override;
bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src,
const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Surface& dst,
const Tegra::Engines::Fermi2D::Config& copy_config) override; const Tegra::Engines::Fermi2D::Config& copy_config) override;
@ -114,6 +120,9 @@ private:
static constexpr VkDeviceSize DEFAULT_BUFFER_SIZE = 4 * sizeof(float); static constexpr VkDeviceSize DEFAULT_BUFFER_SIZE = 4 * sizeof(float);
template <typename Func>
void PrepareDraw(bool is_indexed, Func&&);
void FlushWork(); void FlushWork();
void UpdateDynamicStates(); void UpdateDynamicStates();
@ -135,9 +144,16 @@ private:
void UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdatePrimitiveRestartEnable(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateRasterizerDiscardEnable(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateDepthBiasEnable(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateLogicOpEnable(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateDepthClampEnable(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateLogicOp(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs);

View file

@ -1,5 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include <algorithm> #include <algorithm>
#include <utility> #include <utility>
@ -30,8 +30,7 @@ constexpr VkDeviceSize MAX_STREAM_BUFFER_REQUEST_SIZE = 8_MiB;
constexpr VkDeviceSize STREAM_BUFFER_SIZE = 128_MiB; constexpr VkDeviceSize STREAM_BUFFER_SIZE = 128_MiB;
constexpr VkDeviceSize REGION_SIZE = STREAM_BUFFER_SIZE / StagingBufferPool::NUM_SYNCS; constexpr VkDeviceSize REGION_SIZE = STREAM_BUFFER_SIZE / StagingBufferPool::NUM_SYNCS;
constexpr VkMemoryPropertyFlags HOST_FLAGS = constexpr VkMemoryPropertyFlags HOST_FLAGS = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
constexpr VkMemoryPropertyFlags STREAM_FLAGS = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | HOST_FLAGS; constexpr VkMemoryPropertyFlags STREAM_FLAGS = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | HOST_FLAGS;
bool IsStreamHeap(VkMemoryHeap heap) noexcept { bool IsStreamHeap(VkMemoryHeap heap) noexcept {
@ -93,8 +92,7 @@ StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& mem
.pNext = nullptr, .pNext = nullptr,
.flags = 0, .flags = 0,
.size = STREAM_BUFFER_SIZE, .size = STREAM_BUFFER_SIZE,
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE, .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0, .queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr, .pQueueFamilyIndices = nullptr,
@ -142,11 +140,23 @@ StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& mem
StagingBufferPool::~StagingBufferPool() = default; StagingBufferPool::~StagingBufferPool() = default;
StagingBufferRef StagingBufferPool::Request(size_t size, MemoryUsage usage) { StagingBufferRef StagingBufferPool::Request(size_t size, MemoryUsage usage, bool deferred) {
if (usage == MemoryUsage::Upload && size <= MAX_STREAM_BUFFER_REQUEST_SIZE) { if (!deferred && usage == MemoryUsage::Upload && size <= MAX_STREAM_BUFFER_REQUEST_SIZE) {
return GetStreamBuffer(size); return GetStreamBuffer(size);
} }
return GetStagingBuffer(size, usage); return GetStagingBuffer(size, usage, deferred);
}
void StagingBufferPool::FreeDeferred(StagingBufferRef& ref) {
auto& entries = GetCache(ref.usage)[ref.log2_level].entries;
const auto is_this_one = [&ref](const StagingBuffer& entry) {
return entry.index == ref.index;
};
auto it = std::find_if(entries.begin(), entries.end(), is_this_one);
ASSERT(it != entries.end());
ASSERT(it->deferred);
it->tick = scheduler.CurrentTick();
it->deferred = false;
} }
void StagingBufferPool::TickFrame() { void StagingBufferPool::TickFrame() {
@ -187,6 +197,9 @@ StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) {
.buffer = *stream_buffer, .buffer = *stream_buffer,
.offset = static_cast<VkDeviceSize>(offset), .offset = static_cast<VkDeviceSize>(offset),
.mapped_span = std::span<u8>(stream_pointer + offset, size), .mapped_span = std::span<u8>(stream_pointer + offset, size),
.usage{},
.log2_level{},
.index{},
}; };
} }
@ -196,19 +209,21 @@ bool StagingBufferPool::AreRegionsActive(size_t region_begin, size_t region_end)
[gpu_tick](u64 sync_tick) { return gpu_tick < sync_tick; }); [gpu_tick](u64 sync_tick) { return gpu_tick < sync_tick; });
}; };
StagingBufferRef StagingBufferPool::GetStagingBuffer(size_t size, MemoryUsage usage) { StagingBufferRef StagingBufferPool::GetStagingBuffer(size_t size, MemoryUsage usage,
if (const std::optional<StagingBufferRef> ref = TryGetReservedBuffer(size, usage)) { bool deferred) {
if (const std::optional<StagingBufferRef> ref = TryGetReservedBuffer(size, usage, deferred)) {
return *ref; return *ref;
} }
return CreateStagingBuffer(size, usage); return CreateStagingBuffer(size, usage, deferred);
} }
std::optional<StagingBufferRef> StagingBufferPool::TryGetReservedBuffer(size_t size, std::optional<StagingBufferRef> StagingBufferPool::TryGetReservedBuffer(size_t size,
MemoryUsage usage) { MemoryUsage usage,
bool deferred) {
StagingBuffers& cache_level = GetCache(usage)[Common::Log2Ceil64(size)]; StagingBuffers& cache_level = GetCache(usage)[Common::Log2Ceil64(size)];
const auto is_free = [this](const StagingBuffer& entry) { const auto is_free = [this](const StagingBuffer& entry) {
return scheduler.IsFree(entry.tick); return !entry.deferred && scheduler.IsFree(entry.tick);
}; };
auto& entries = cache_level.entries; auto& entries = cache_level.entries;
const auto hint_it = entries.begin() + cache_level.iterate_index; const auto hint_it = entries.begin() + cache_level.iterate_index;
@ -220,20 +235,28 @@ std::optional<StagingBufferRef> StagingBufferPool::TryGetReservedBuffer(size_t s
} }
} }
cache_level.iterate_index = std::distance(entries.begin(), it) + 1; cache_level.iterate_index = std::distance(entries.begin(), it) + 1;
it->tick = scheduler.CurrentTick(); it->tick = deferred ? std::numeric_limits<u64>::max() : scheduler.CurrentTick();
ASSERT(!it->deferred);
it->deferred = deferred;
return it->Ref(); return it->Ref();
} }
StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage usage) { StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage usage,
bool deferred) {
const u32 log2 = Common::Log2Ceil64(size); const u32 log2 = Common::Log2Ceil64(size);
VkBufferUsageFlags usage_flags{};
if (usage == MemoryUsage::Upload) {
usage_flags |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
}
if (usage == MemoryUsage::Download) {
usage_flags |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
}
vk::Buffer buffer = device.GetLogical().CreateBuffer({ vk::Buffer buffer = device.GetLogical().CreateBuffer({
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr, .pNext = nullptr,
.flags = 0, .flags = 0,
.size = 1ULL << log2, .size = 1ULL << log2,
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | .usage = usage_flags,
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE, .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0, .queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr, .pQueueFamilyIndices = nullptr,
@ -249,7 +272,11 @@ StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage
.buffer = std::move(buffer), .buffer = std::move(buffer),
.commit = std::move(commit), .commit = std::move(commit),
.mapped_span = mapped_span, .mapped_span = mapped_span,
.tick = scheduler.CurrentTick(), .usage = usage,
.log2_level = log2,
.index = unique_ids++,
.tick = deferred ? std::numeric_limits<u64>::max() : scheduler.CurrentTick(),
.deferred = deferred,
}); });
return entry.Ref(); return entry.Ref();
} }

View file

@ -1,5 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#pragma once #pragma once
@ -20,6 +20,9 @@ struct StagingBufferRef {
VkBuffer buffer; VkBuffer buffer;
VkDeviceSize offset; VkDeviceSize offset;
std::span<u8> mapped_span; std::span<u8> mapped_span;
MemoryUsage usage;
u32 log2_level;
u64 index;
}; };
class StagingBufferPool { class StagingBufferPool {
@ -30,7 +33,8 @@ public:
Scheduler& scheduler); Scheduler& scheduler);
~StagingBufferPool(); ~StagingBufferPool();
StagingBufferRef Request(size_t size, MemoryUsage usage); StagingBufferRef Request(size_t size, MemoryUsage usage, bool deferred = false);
void FreeDeferred(StagingBufferRef& ref);
void TickFrame(); void TickFrame();
@ -44,13 +48,20 @@ private:
vk::Buffer buffer; vk::Buffer buffer;
MemoryCommit commit; MemoryCommit commit;
std::span<u8> mapped_span; std::span<u8> mapped_span;
MemoryUsage usage;
u32 log2_level;
u64 index;
u64 tick = 0; u64 tick = 0;
bool deferred{};
StagingBufferRef Ref() const noexcept { StagingBufferRef Ref() const noexcept {
return { return {
.buffer = *buffer, .buffer = *buffer,
.offset = 0, .offset = 0,
.mapped_span = mapped_span, .mapped_span = mapped_span,
.usage = usage,
.log2_level = log2_level,
.index = index,
}; };
} }
}; };
@ -68,11 +79,12 @@ private:
bool AreRegionsActive(size_t region_begin, size_t region_end) const; bool AreRegionsActive(size_t region_begin, size_t region_end) const;
StagingBufferRef GetStagingBuffer(size_t size, MemoryUsage usage); StagingBufferRef GetStagingBuffer(size_t size, MemoryUsage usage, bool deferred = false);
std::optional<StagingBufferRef> TryGetReservedBuffer(size_t size, MemoryUsage usage); std::optional<StagingBufferRef> TryGetReservedBuffer(size_t size, MemoryUsage usage,
bool deferred);
StagingBufferRef CreateStagingBuffer(size_t size, MemoryUsage usage); StagingBufferRef CreateStagingBuffer(size_t size, MemoryUsage usage, bool deferred);
StagingBuffersCache& GetCache(MemoryUsage usage); StagingBuffersCache& GetCache(MemoryUsage usage);
@ -99,6 +111,7 @@ private:
size_t current_delete_level = 0; size_t current_delete_level = 0;
u64 buffer_index = 0; u64 buffer_index = 0;
u64 unique_ids{};
}; };
} // namespace Vulkan } // namespace Vulkan

View file

@ -27,10 +27,34 @@ using Flags = Maxwell3D::DirtyState::Flags;
Flags MakeInvalidationFlags() { Flags MakeInvalidationFlags() {
static constexpr int INVALIDATION_FLAGS[]{ static constexpr int INVALIDATION_FLAGS[]{
Viewports, Scissors, DepthBias, BlendConstants, DepthBounds, Viewports,
StencilProperties, LineWidth, CullMode, DepthBoundsEnable, DepthTestEnable, Scissors,
DepthWriteEnable, DepthCompareOp, FrontFace, StencilOp, StencilTestEnable, DepthBias,
VertexBuffers, VertexInput, BlendConstants,
DepthBounds,
StencilProperties,
LineWidth,
CullMode,
DepthBoundsEnable,
DepthTestEnable,
DepthWriteEnable,
DepthCompareOp,
FrontFace,
StencilOp,
StencilTestEnable,
VertexBuffers,
VertexInput,
StateEnable,
PrimitiveRestartEnable,
RasterizerDiscardEnable,
DepthBiasEnable,
LogicOpEnable,
DepthClampEnable,
LogicOp,
Blending,
ColorMask,
BlendEquations,
BlendEnable,
}; };
Flags flags{}; Flags flags{};
for (const int flag : INVALIDATION_FLAGS) { for (const int flag : INVALIDATION_FLAGS) {
@ -96,16 +120,22 @@ void SetupDirtyCullMode(Tables& tables) {
table[OFF(gl_cull_test_enabled)] = CullMode; table[OFF(gl_cull_test_enabled)] = CullMode;
} }
void SetupDirtyDepthBoundsEnable(Tables& tables) { void SetupDirtyStateEnable(Tables& tables) {
tables[0][OFF(depth_bounds_enable)] = DepthBoundsEnable; const auto setup = [&](size_t position, u8 flag) {
} tables[0][position] = flag;
tables[1][position] = StateEnable;
void SetupDirtyDepthTestEnable(Tables& tables) { };
tables[0][OFF(depth_test_enable)] = DepthTestEnable; setup(OFF(depth_bounds_enable), DepthBoundsEnable);
} setup(OFF(depth_test_enable), DepthTestEnable);
setup(OFF(depth_write_enabled), DepthWriteEnable);
void SetupDirtyDepthWriteEnable(Tables& tables) { setup(OFF(stencil_enable), StencilTestEnable);
tables[0][OFF(depth_write_enabled)] = DepthWriteEnable; setup(OFF(primitive_restart.enabled), PrimitiveRestartEnable);
setup(OFF(rasterize_enable), RasterizerDiscardEnable);
setup(OFF(polygon_offset_point_enable), DepthBiasEnable);
setup(OFF(polygon_offset_line_enable), DepthBiasEnable);
setup(OFF(polygon_offset_fill_enable), DepthBiasEnable);
setup(OFF(logic_op.enable), LogicOpEnable);
setup(OFF(viewport_clip_control.geometry_clip), DepthClampEnable);
} }
void SetupDirtyDepthCompareOp(Tables& tables) { void SetupDirtyDepthCompareOp(Tables& tables) {
@ -133,16 +163,22 @@ void SetupDirtyStencilOp(Tables& tables) {
tables[1][OFF(stencil_two_side_enable)] = StencilOp; tables[1][OFF(stencil_two_side_enable)] = StencilOp;
} }
void SetupDirtyStencilTestEnable(Tables& tables) {
tables[0][OFF(stencil_enable)] = StencilTestEnable;
}
void SetupDirtyBlending(Tables& tables) { void SetupDirtyBlending(Tables& tables) {
tables[0][OFF(color_mask_common)] = Blending; tables[0][OFF(color_mask_common)] = Blending;
tables[1][OFF(color_mask_common)] = ColorMask;
tables[0][OFF(blend_per_target_enabled)] = Blending; tables[0][OFF(blend_per_target_enabled)] = Blending;
tables[1][OFF(blend_per_target_enabled)] = BlendEquations;
FillBlock(tables[0], OFF(color_mask), NUM(color_mask), Blending); FillBlock(tables[0], OFF(color_mask), NUM(color_mask), Blending);
FillBlock(tables[1], OFF(color_mask), NUM(color_mask), ColorMask);
FillBlock(tables[0], OFF(blend), NUM(blend), Blending); FillBlock(tables[0], OFF(blend), NUM(blend), Blending);
FillBlock(tables[1], OFF(blend), NUM(blend), BlendEquations);
FillBlock(tables[1], OFF(blend.enable), NUM(blend.enable), BlendEnable);
FillBlock(tables[0], OFF(blend_per_target), NUM(blend_per_target), Blending); FillBlock(tables[0], OFF(blend_per_target), NUM(blend_per_target), Blending);
FillBlock(tables[1], OFF(blend_per_target), NUM(blend_per_target), BlendEquations);
}
void SetupDirtySpecialOps(Tables& tables) {
tables[0][OFF(logic_op.op)] = LogicOp;
} }
void SetupDirtyViewportSwizzles(Tables& tables) { void SetupDirtyViewportSwizzles(Tables& tables) {
@ -185,17 +221,15 @@ void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) {
SetupDirtyStencilProperties(tables); SetupDirtyStencilProperties(tables);
SetupDirtyLineWidth(tables); SetupDirtyLineWidth(tables);
SetupDirtyCullMode(tables); SetupDirtyCullMode(tables);
SetupDirtyDepthBoundsEnable(tables); SetupDirtyStateEnable(tables);
SetupDirtyDepthTestEnable(tables);
SetupDirtyDepthWriteEnable(tables);
SetupDirtyDepthCompareOp(tables); SetupDirtyDepthCompareOp(tables);
SetupDirtyFrontFace(tables); SetupDirtyFrontFace(tables);
SetupDirtyStencilOp(tables); SetupDirtyStencilOp(tables);
SetupDirtyStencilTestEnable(tables);
SetupDirtyBlending(tables); SetupDirtyBlending(tables);
SetupDirtyViewportSwizzles(tables); SetupDirtyViewportSwizzles(tables);
SetupDirtyVertexAttributes(tables); SetupDirtyVertexAttributes(tables);
SetupDirtyVertexBindings(tables); SetupDirtyVertexBindings(tables);
SetupDirtySpecialOps(tables);
} }
void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) { void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) {

View file

@ -45,8 +45,18 @@ enum : u8 {
FrontFace, FrontFace,
StencilOp, StencilOp,
StencilTestEnable, StencilTestEnable,
PrimitiveRestartEnable,
RasterizerDiscardEnable,
DepthBiasEnable,
StateEnable,
LogicOp,
LogicOpEnable,
DepthClampEnable,
Blending, Blending,
BlendEnable,
BlendEquations,
ColorMask,
ViewportSwizzles, ViewportSwizzles,
Last, Last,
@ -111,6 +121,10 @@ public:
return Exchange(Dirty::CullMode, false); return Exchange(Dirty::CullMode, false);
} }
bool TouchStateEnable() {
return Exchange(Dirty::StateEnable, false);
}
bool TouchDepthBoundsTestEnable() { bool TouchDepthBoundsTestEnable() {
return Exchange(Dirty::DepthBoundsEnable, false); return Exchange(Dirty::DepthBoundsEnable, false);
} }
@ -123,6 +137,26 @@ public:
return Exchange(Dirty::DepthWriteEnable, false); return Exchange(Dirty::DepthWriteEnable, false);
} }
bool TouchPrimitiveRestartEnable() {
return Exchange(Dirty::PrimitiveRestartEnable, false);
}
bool TouchRasterizerDiscardEnable() {
return Exchange(Dirty::RasterizerDiscardEnable, false);
}
bool TouchDepthBiasEnable() {
return Exchange(Dirty::DepthBiasEnable, false);
}
bool TouchLogicOpEnable() {
return Exchange(Dirty::LogicOpEnable, false);
}
bool TouchDepthClampEnable() {
return Exchange(Dirty::DepthClampEnable, false);
}
bool TouchDepthCompareOp() { bool TouchDepthCompareOp() {
return Exchange(Dirty::DepthCompareOp, false); return Exchange(Dirty::DepthCompareOp, false);
} }
@ -135,10 +169,30 @@ public:
return Exchange(Dirty::StencilOp, false); return Exchange(Dirty::StencilOp, false);
} }
bool TouchBlending() {
return Exchange(Dirty::Blending, false);
}
bool TouchBlendEnable() {
return Exchange(Dirty::BlendEnable, false);
}
bool TouchBlendEquations() {
return Exchange(Dirty::BlendEquations, false);
}
bool TouchColorMask() {
return Exchange(Dirty::ColorMask, false);
}
bool TouchStencilTestEnable() { bool TouchStencilTestEnable() {
return Exchange(Dirty::StencilTestEnable, false); return Exchange(Dirty::StencilTestEnable, false);
} }
bool TouchLogicOp() {
return Exchange(Dirty::LogicOp, false);
}
bool ChangePrimitiveTopology(Maxwell::PrimitiveTopology new_topology) { bool ChangePrimitiveTopology(Maxwell::PrimitiveTopology new_topology) {
const bool has_changed = current_topology != new_topology; const bool has_changed = current_topology != new_topology;
current_topology = new_topology; current_topology = new_topology;

View file

@ -202,12 +202,15 @@ void GenericEnvironment::Serialize(std::ofstream& file) const {
const u64 num_texture_types{static_cast<u64>(texture_types.size())}; const u64 num_texture_types{static_cast<u64>(texture_types.size())};
const u64 num_texture_pixel_formats{static_cast<u64>(texture_pixel_formats.size())}; const u64 num_texture_pixel_formats{static_cast<u64>(texture_pixel_formats.size())};
const u64 num_cbuf_values{static_cast<u64>(cbuf_values.size())}; const u64 num_cbuf_values{static_cast<u64>(cbuf_values.size())};
const u64 num_cbuf_replacement_values{static_cast<u64>(cbuf_replacements.size())};
file.write(reinterpret_cast<const char*>(&code_size), sizeof(code_size)) file.write(reinterpret_cast<const char*>(&code_size), sizeof(code_size))
.write(reinterpret_cast<const char*>(&num_texture_types), sizeof(num_texture_types)) .write(reinterpret_cast<const char*>(&num_texture_types), sizeof(num_texture_types))
.write(reinterpret_cast<const char*>(&num_texture_pixel_formats), .write(reinterpret_cast<const char*>(&num_texture_pixel_formats),
sizeof(num_texture_pixel_formats)) sizeof(num_texture_pixel_formats))
.write(reinterpret_cast<const char*>(&num_cbuf_values), sizeof(num_cbuf_values)) .write(reinterpret_cast<const char*>(&num_cbuf_values), sizeof(num_cbuf_values))
.write(reinterpret_cast<const char*>(&num_cbuf_replacement_values),
sizeof(num_cbuf_replacement_values))
.write(reinterpret_cast<const char*>(&local_memory_size), sizeof(local_memory_size)) .write(reinterpret_cast<const char*>(&local_memory_size), sizeof(local_memory_size))
.write(reinterpret_cast<const char*>(&texture_bound), sizeof(texture_bound)) .write(reinterpret_cast<const char*>(&texture_bound), sizeof(texture_bound))
.write(reinterpret_cast<const char*>(&start_address), sizeof(start_address)) .write(reinterpret_cast<const char*>(&start_address), sizeof(start_address))
@ -229,6 +232,10 @@ void GenericEnvironment::Serialize(std::ofstream& file) const {
file.write(reinterpret_cast<const char*>(&key), sizeof(key)) file.write(reinterpret_cast<const char*>(&key), sizeof(key))
.write(reinterpret_cast<const char*>(&type), sizeof(type)); .write(reinterpret_cast<const char*>(&type), sizeof(type));
} }
for (const auto& [key, type] : cbuf_replacements) {
file.write(reinterpret_cast<const char*>(&key), sizeof(key))
.write(reinterpret_cast<const char*>(&type), sizeof(type));
}
if (stage == Shader::Stage::Compute) { if (stage == Shader::Stage::Compute) {
file.write(reinterpret_cast<const char*>(&workgroup_size), sizeof(workgroup_size)) file.write(reinterpret_cast<const char*>(&workgroup_size), sizeof(workgroup_size))
.write(reinterpret_cast<const char*>(&shared_memory_size), sizeof(shared_memory_size)); .write(reinterpret_cast<const char*>(&shared_memory_size), sizeof(shared_memory_size));
@ -318,6 +325,8 @@ GraphicsEnvironment::GraphicsEnvironment(Tegra::Engines::Maxwell3D& maxwell3d_,
ASSERT(local_size <= std::numeric_limits<u32>::max()); ASSERT(local_size <= std::numeric_limits<u32>::max());
local_memory_size = static_cast<u32>(local_size) + sph.common3.shader_local_memory_crs_size; local_memory_size = static_cast<u32>(local_size) + sph.common3.shader_local_memory_crs_size;
texture_bound = maxwell3d->regs.bindless_texture_const_buffer_slot; texture_bound = maxwell3d->regs.bindless_texture_const_buffer_slot;
has_hle_engine_state =
maxwell3d->engine_state == Tegra::Engines::Maxwell3D::EngineHint::OnHLEMacro;
} }
u32 GraphicsEnvironment::ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) { u32 GraphicsEnvironment::ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) {
@ -331,6 +340,30 @@ u32 GraphicsEnvironment::ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) {
return value; return value;
} }
std::optional<Shader::ReplaceConstant> GraphicsEnvironment::GetReplaceConstBuffer(u32 bank,
u32 offset) {
if (!has_hle_engine_state) {
return std::nullopt;
}
const u64 key = (static_cast<u64>(bank) << 32) | static_cast<u64>(offset);
auto it = maxwell3d->replace_table.find(key);
if (it == maxwell3d->replace_table.end()) {
return std::nullopt;
}
const auto converted_value = [](Tegra::Engines::Maxwell3D::HLEReplaceName name) {
switch (name) {
case Tegra::Engines::Maxwell3D::HLEReplaceName::BaseVertex:
return Shader::ReplaceConstant::BaseVertex;
case Tegra::Engines::Maxwell3D::HLEReplaceName::BaseInstance:
return Shader::ReplaceConstant::BaseInstance;
default:
UNREACHABLE();
}
}(it->second);
cbuf_replacements.emplace(key, converted_value);
return converted_value;
}
Shader::TextureType GraphicsEnvironment::ReadTextureType(u32 handle) { Shader::TextureType GraphicsEnvironment::ReadTextureType(u32 handle) {
const auto& regs{maxwell3d->regs}; const auto& regs{maxwell3d->regs};
const bool via_header_index{regs.sampler_binding == Maxwell::SamplerBinding::ViaHeaderBinding}; const bool via_header_index{regs.sampler_binding == Maxwell::SamplerBinding::ViaHeaderBinding};
@ -409,11 +442,14 @@ void FileEnvironment::Deserialize(std::ifstream& file) {
u64 num_texture_types{}; u64 num_texture_types{};
u64 num_texture_pixel_formats{}; u64 num_texture_pixel_formats{};
u64 num_cbuf_values{}; u64 num_cbuf_values{};
u64 num_cbuf_replacement_values{};
file.read(reinterpret_cast<char*>(&code_size), sizeof(code_size)) file.read(reinterpret_cast<char*>(&code_size), sizeof(code_size))
.read(reinterpret_cast<char*>(&num_texture_types), sizeof(num_texture_types)) .read(reinterpret_cast<char*>(&num_texture_types), sizeof(num_texture_types))
.read(reinterpret_cast<char*>(&num_texture_pixel_formats), .read(reinterpret_cast<char*>(&num_texture_pixel_formats),
sizeof(num_texture_pixel_formats)) sizeof(num_texture_pixel_formats))
.read(reinterpret_cast<char*>(&num_cbuf_values), sizeof(num_cbuf_values)) .read(reinterpret_cast<char*>(&num_cbuf_values), sizeof(num_cbuf_values))
.read(reinterpret_cast<char*>(&num_cbuf_replacement_values),
sizeof(num_cbuf_replacement_values))
.read(reinterpret_cast<char*>(&local_memory_size), sizeof(local_memory_size)) .read(reinterpret_cast<char*>(&local_memory_size), sizeof(local_memory_size))
.read(reinterpret_cast<char*>(&texture_bound), sizeof(texture_bound)) .read(reinterpret_cast<char*>(&texture_bound), sizeof(texture_bound))
.read(reinterpret_cast<char*>(&start_address), sizeof(start_address)) .read(reinterpret_cast<char*>(&start_address), sizeof(start_address))
@ -444,6 +480,13 @@ void FileEnvironment::Deserialize(std::ifstream& file) {
.read(reinterpret_cast<char*>(&value), sizeof(value)); .read(reinterpret_cast<char*>(&value), sizeof(value));
cbuf_values.emplace(key, value); cbuf_values.emplace(key, value);
} }
for (size_t i = 0; i < num_cbuf_replacement_values; ++i) {
u64 key;
Shader::ReplaceConstant value;
file.read(reinterpret_cast<char*>(&key), sizeof(key))
.read(reinterpret_cast<char*>(&value), sizeof(value));
cbuf_replacements.emplace(key, value);
}
if (stage == Shader::Stage::Compute) { if (stage == Shader::Stage::Compute) {
file.read(reinterpret_cast<char*>(&workgroup_size), sizeof(workgroup_size)) file.read(reinterpret_cast<char*>(&workgroup_size), sizeof(workgroup_size))
.read(reinterpret_cast<char*>(&shared_memory_size), sizeof(shared_memory_size)); .read(reinterpret_cast<char*>(&shared_memory_size), sizeof(shared_memory_size));
@ -512,6 +555,16 @@ std::array<u32, 3> FileEnvironment::WorkgroupSize() const {
return workgroup_size; return workgroup_size;
} }
std::optional<Shader::ReplaceConstant> FileEnvironment::GetReplaceConstBuffer(u32 bank,
u32 offset) {
const u64 key = (static_cast<u64>(bank) << 32) | static_cast<u64>(offset);
auto it = cbuf_replacements.find(key);
if (it == cbuf_replacements.end()) {
return std::nullopt;
}
return it->second;
}
void SerializePipeline(std::span<const char> key, std::span<const GenericEnvironment* const> envs, void SerializePipeline(std::span<const char> key, std::span<const GenericEnvironment* const> envs,
const std::filesystem::path& filename, u32 cache_version) try { const std::filesystem::path& filename, u32 cache_version) try {
std::ofstream file(filename, std::ios::binary | std::ios::ate | std::ios::app); std::ofstream file(filename, std::ios::binary | std::ios::ate | std::ios::app);

View file

@ -60,6 +60,10 @@ public:
void Serialize(std::ofstream& file) const; void Serialize(std::ofstream& file) const;
bool HasHLEMacroState() const override {
return has_hle_engine_state;
}
protected: protected:
std::optional<u64> TryFindSize(); std::optional<u64> TryFindSize();
@ -73,6 +77,7 @@ protected:
std::unordered_map<u32, Shader::TextureType> texture_types; std::unordered_map<u32, Shader::TextureType> texture_types;
std::unordered_map<u32, Shader::TexturePixelFormat> texture_pixel_formats; std::unordered_map<u32, Shader::TexturePixelFormat> texture_pixel_formats;
std::unordered_map<u64, u32> cbuf_values; std::unordered_map<u64, u32> cbuf_values;
std::unordered_map<u64, Shader::ReplaceConstant> cbuf_replacements;
u32 local_memory_size{}; u32 local_memory_size{};
u32 texture_bound{}; u32 texture_bound{};
@ -89,6 +94,7 @@ protected:
u32 viewport_transform_state = 1; u32 viewport_transform_state = 1;
bool has_unbound_instructions = false; bool has_unbound_instructions = false;
bool has_hle_engine_state = false;
}; };
class GraphicsEnvironment final : public GenericEnvironment { class GraphicsEnvironment final : public GenericEnvironment {
@ -109,6 +115,8 @@ public:
u32 ReadViewportTransformState() override; u32 ReadViewportTransformState() override;
std::optional<Shader::ReplaceConstant> GetReplaceConstBuffer(u32 bank, u32 offset) override;
private: private:
Tegra::Engines::Maxwell3D* maxwell3d{}; Tegra::Engines::Maxwell3D* maxwell3d{};
size_t stage_index{}; size_t stage_index{};
@ -131,6 +139,11 @@ public:
u32 ReadViewportTransformState() override; u32 ReadViewportTransformState() override;
std::optional<Shader::ReplaceConstant> GetReplaceConstBuffer(
[[maybe_unused]] u32 bank, [[maybe_unused]] u32 offset) override {
return std::nullopt;
}
private: private:
Tegra::Engines::KeplerCompute* kepler_compute{}; Tegra::Engines::KeplerCompute* kepler_compute{};
}; };
@ -166,6 +179,13 @@ public:
[[nodiscard]] std::array<u32, 3> WorkgroupSize() const override; [[nodiscard]] std::array<u32, 3> WorkgroupSize() const override;
[[nodiscard]] std::optional<Shader::ReplaceConstant> GetReplaceConstBuffer(u32 bank,
u32 offset) override;
[[nodiscard]] bool HasHLEMacroState() const override {
return cbuf_replacements.size() != 0;
}
void Dump(u64 hash) override; void Dump(u64 hash) override;
private: private:
@ -173,6 +193,7 @@ private:
std::unordered_map<u32, Shader::TextureType> texture_types; std::unordered_map<u32, Shader::TextureType> texture_types;
std::unordered_map<u32, Shader::TexturePixelFormat> texture_pixel_formats; std::unordered_map<u32, Shader::TexturePixelFormat> texture_pixel_formats;
std::unordered_map<u64, u32> cbuf_values; std::unordered_map<u64, u32> cbuf_values;
std::unordered_map<u64, Shader::ReplaceConstant> cbuf_replacements;
std::array<u32, 3> workgroup_size{}; std::array<u32, 3> workgroup_size{};
u32 local_memory_size{}; u32 local_memory_size{};
u32 shared_memory_size{}; u32 shared_memory_size{};

View file

@ -740,7 +740,8 @@ void TextureCache<P>::UploadImageContents(Image& image, StagingBuffer& staging)
const GPUVAddr gpu_addr = image.gpu_addr; const GPUVAddr gpu_addr = image.gpu_addr;
if (True(image.flags & ImageFlagBits::AcceleratedUpload)) { if (True(image.flags & ImageFlagBits::AcceleratedUpload)) {
gpu_memory->ReadBlockUnsafe(gpu_addr, mapped_span.data(), mapped_span.size_bytes()); gpu_memory->ReadBlock(gpu_addr, mapped_span.data(), mapped_span.size_bytes(),
VideoCommon::CacheType::NoTextureCache);
const auto uploads = FullUploadSwizzles(image.info); const auto uploads = FullUploadSwizzles(image.info);
runtime.AccelerateImageUpload(image, staging, uploads); runtime.AccelerateImageUpload(image, staging, uploads);
return; return;

View file

@ -202,7 +202,7 @@ public:
/// Create channel state. /// Create channel state.
void CreateChannel(Tegra::Control::ChannelState& channel) final override; void CreateChannel(Tegra::Control::ChannelState& channel) final override;
std::mutex mutex; std::recursive_mutex mutex;
private: private:
/// Iterate over all page indices in a range /// Iterate over all page indices in a range

View file

@ -350,8 +350,8 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
.sampleRateShading = true, .sampleRateShading = true,
.dualSrcBlend = true, .dualSrcBlend = true,
.logicOp = true, .logicOp = true,
.multiDrawIndirect = false, .multiDrawIndirect = true,
.drawIndirectFirstInstance = false, .drawIndirectFirstInstance = true,
.depthClamp = true, .depthClamp = true,
.depthBiasClamp = true, .depthBiasClamp = true,
.fillModeNonSolid = true, .fillModeNonSolid = true,
@ -569,6 +569,67 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
LOG_INFO(Render_Vulkan, "Device doesn't support extended dynamic state"); LOG_INFO(Render_Vulkan, "Device doesn't support extended dynamic state");
} }
VkPhysicalDeviceExtendedDynamicState2FeaturesEXT dynamic_state_2;
if (ext_extended_dynamic_state_2) {
dynamic_state_2 = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_2_FEATURES_EXT,
.pNext = nullptr,
.extendedDynamicState2 = VK_TRUE,
.extendedDynamicState2LogicOp = ext_extended_dynamic_state_2_extra ? VK_TRUE : VK_FALSE,
.extendedDynamicState2PatchControlPoints = VK_FALSE,
};
SetNext(next, dynamic_state_2);
} else {
LOG_INFO(Render_Vulkan, "Device doesn't support extended dynamic state 2");
}
VkPhysicalDeviceExtendedDynamicState3FeaturesEXT dynamic_state_3;
if (ext_extended_dynamic_state_3) {
dynamic_state_3 = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_3_FEATURES_EXT,
.pNext = nullptr,
.extendedDynamicState3TessellationDomainOrigin = VK_FALSE,
.extendedDynamicState3DepthClampEnable =
ext_extended_dynamic_state_3_enables ? VK_TRUE : VK_FALSE,
.extendedDynamicState3PolygonMode = VK_FALSE,
.extendedDynamicState3RasterizationSamples = VK_FALSE,
.extendedDynamicState3SampleMask = VK_FALSE,
.extendedDynamicState3AlphaToCoverageEnable = VK_FALSE,
.extendedDynamicState3AlphaToOneEnable = VK_FALSE,
.extendedDynamicState3LogicOpEnable =
ext_extended_dynamic_state_3_enables ? VK_TRUE : VK_FALSE,
.extendedDynamicState3ColorBlendEnable =
ext_extended_dynamic_state_3_blend ? VK_TRUE : VK_FALSE,
.extendedDynamicState3ColorBlendEquation =
ext_extended_dynamic_state_3_blend ? VK_TRUE : VK_FALSE,
.extendedDynamicState3ColorWriteMask =
ext_extended_dynamic_state_3_blend ? VK_TRUE : VK_FALSE,
.extendedDynamicState3RasterizationStream = VK_FALSE,
.extendedDynamicState3ConservativeRasterizationMode = VK_FALSE,
.extendedDynamicState3ExtraPrimitiveOverestimationSize = VK_FALSE,
.extendedDynamicState3DepthClipEnable = VK_FALSE,
.extendedDynamicState3SampleLocationsEnable = VK_FALSE,
.extendedDynamicState3ColorBlendAdvanced = VK_FALSE,
.extendedDynamicState3ProvokingVertexMode = VK_FALSE,
.extendedDynamicState3LineRasterizationMode = VK_FALSE,
.extendedDynamicState3LineStippleEnable = VK_FALSE,
.extendedDynamicState3DepthClipNegativeOneToOne = VK_FALSE,
.extendedDynamicState3ViewportWScalingEnable = VK_FALSE,
.extendedDynamicState3ViewportSwizzle = VK_FALSE,
.extendedDynamicState3CoverageToColorEnable = VK_FALSE,
.extendedDynamicState3CoverageToColorLocation = VK_FALSE,
.extendedDynamicState3CoverageModulationMode = VK_FALSE,
.extendedDynamicState3CoverageModulationTableEnable = VK_FALSE,
.extendedDynamicState3CoverageModulationTable = VK_FALSE,
.extendedDynamicState3CoverageReductionMode = VK_FALSE,
.extendedDynamicState3RepresentativeFragmentTestEnable = VK_FALSE,
.extendedDynamicState3ShadingRateImageEnable = VK_FALSE,
};
SetNext(next, dynamic_state_3);
} else {
LOG_INFO(Render_Vulkan, "Device doesn't support extended dynamic state 3");
}
VkPhysicalDeviceLineRasterizationFeaturesEXT line_raster; VkPhysicalDeviceLineRasterizationFeaturesEXT line_raster;
if (ext_line_rasterization) { if (ext_line_rasterization) {
line_raster = { line_raster = {
@ -735,6 +796,16 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
ext_vertex_input_dynamic_state = false; ext_vertex_input_dynamic_state = false;
} }
} }
if (ext_extended_dynamic_state_2 && is_radv) {
const u32 version = (properties.driverVersion << 3) >> 3;
if (version < VK_MAKE_API_VERSION(0, 22, 3, 1)) {
LOG_WARNING(
Render_Vulkan,
"RADV versions older than 22.3.1 have broken VK_EXT_extended_dynamic_state2");
ext_extended_dynamic_state_2 = false;
ext_extended_dynamic_state_2_extra = false;
}
}
sets_per_pool = 64; sets_per_pool = 64;
const bool is_amd = const bool is_amd =
@ -1024,6 +1095,8 @@ void Device::CheckSuitability(bool requires_swapchain) const {
std::make_pair(features.vertexPipelineStoresAndAtomics, "vertexPipelineStoresAndAtomics"), std::make_pair(features.vertexPipelineStoresAndAtomics, "vertexPipelineStoresAndAtomics"),
std::make_pair(features.imageCubeArray, "imageCubeArray"), std::make_pair(features.imageCubeArray, "imageCubeArray"),
std::make_pair(features.independentBlend, "independentBlend"), std::make_pair(features.independentBlend, "independentBlend"),
std::make_pair(features.multiDrawIndirect, "multiDrawIndirect"),
std::make_pair(features.drawIndirectFirstInstance, "drawIndirectFirstInstance"),
std::make_pair(features.depthClamp, "depthClamp"), std::make_pair(features.depthClamp, "depthClamp"),
std::make_pair(features.samplerAnisotropy, "samplerAnisotropy"), std::make_pair(features.samplerAnisotropy, "samplerAnisotropy"),
std::make_pair(features.largePoints, "largePoints"), std::make_pair(features.largePoints, "largePoints"),
@ -1089,6 +1162,8 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
bool has_ext_transform_feedback{}; bool has_ext_transform_feedback{};
bool has_ext_custom_border_color{}; bool has_ext_custom_border_color{};
bool has_ext_extended_dynamic_state{}; bool has_ext_extended_dynamic_state{};
bool has_ext_extended_dynamic_state_2{};
bool has_ext_extended_dynamic_state_3{};
bool has_ext_shader_atomic_int64{}; bool has_ext_shader_atomic_int64{};
bool has_ext_provoking_vertex{}; bool has_ext_provoking_vertex{};
bool has_ext_vertex_input_dynamic_state{}; bool has_ext_vertex_input_dynamic_state{};
@ -1117,6 +1192,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
test(khr_spirv_1_4, VK_KHR_SPIRV_1_4_EXTENSION_NAME, true); test(khr_spirv_1_4, VK_KHR_SPIRV_1_4_EXTENSION_NAME, true);
test(khr_push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME, true); test(khr_push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME, true);
test(has_khr_shader_float16_int8, VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, false); test(has_khr_shader_float16_int8, VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, false);
test(khr_draw_indirect_count, VK_KHR_DRAW_INDIRECT_COUNT_EXTENSION_NAME, true);
test(ext_depth_range_unrestricted, VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME, true); test(ext_depth_range_unrestricted, VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME, true);
test(ext_index_type_uint8, VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME, true); test(ext_index_type_uint8, VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME, true);
test(has_ext_primitive_topology_list_restart, test(has_ext_primitive_topology_list_restart,
@ -1132,6 +1208,10 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
test(has_ext_transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME, false); test(has_ext_transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME, false);
test(has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, false); test(has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, false);
test(has_ext_extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false); test(has_ext_extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false);
test(has_ext_extended_dynamic_state_2, VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME,
false);
test(has_ext_extended_dynamic_state_3, VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME,
false);
test(has_ext_subgroup_size_control, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, true); test(has_ext_subgroup_size_control, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, true);
test(has_ext_provoking_vertex, VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME, false); test(has_ext_provoking_vertex, VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME, false);
test(has_ext_vertex_input_dynamic_state, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME, test(has_ext_vertex_input_dynamic_state, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME,
@ -1281,6 +1361,44 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
ext_extended_dynamic_state = true; ext_extended_dynamic_state = true;
} }
} }
if (has_ext_extended_dynamic_state_2) {
VkPhysicalDeviceExtendedDynamicState2FeaturesEXT extended_dynamic_state_2;
extended_dynamic_state_2.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_2_FEATURES_EXT;
extended_dynamic_state_2.pNext = nullptr;
features.pNext = &extended_dynamic_state_2;
physical.GetFeatures2(features);
if (extended_dynamic_state_2.extendedDynamicState2) {
extensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
ext_extended_dynamic_state_2 = true;
ext_extended_dynamic_state_2_extra =
extended_dynamic_state_2.extendedDynamicState2LogicOp;
}
}
if (has_ext_extended_dynamic_state_3) {
VkPhysicalDeviceExtendedDynamicState3FeaturesEXT extended_dynamic_state_3;
extended_dynamic_state_3.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_3_FEATURES_EXT;
extended_dynamic_state_3.pNext = nullptr;
features.pNext = &extended_dynamic_state_3;
physical.GetFeatures2(features);
ext_extended_dynamic_state_3_blend =
extended_dynamic_state_3.extendedDynamicState3ColorBlendEnable &&
extended_dynamic_state_3.extendedDynamicState3ColorBlendEquation &&
extended_dynamic_state_3.extendedDynamicState3ColorWriteMask;
ext_extended_dynamic_state_3_enables =
extended_dynamic_state_3.extendedDynamicState3DepthClampEnable &&
extended_dynamic_state_3.extendedDynamicState3LogicOpEnable;
ext_extended_dynamic_state_3 =
ext_extended_dynamic_state_3_blend || ext_extended_dynamic_state_3_enables;
if (ext_extended_dynamic_state_3) {
extensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME);
}
}
if (has_ext_line_rasterization) { if (has_ext_line_rasterization) {
VkPhysicalDeviceLineRasterizationFeaturesEXT line_raster; VkPhysicalDeviceLineRasterizationFeaturesEXT line_raster;
line_raster.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT; line_raster.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT;

View file

@ -286,6 +286,30 @@ public:
return ext_extended_dynamic_state; return ext_extended_dynamic_state;
} }
/// Returns true if the device supports VK_EXT_extended_dynamic_state2.
bool IsExtExtendedDynamicState2Supported() const {
return ext_extended_dynamic_state_2;
}
bool IsExtExtendedDynamicState2ExtrasSupported() const {
return ext_extended_dynamic_state_2_extra;
}
/// Returns true if the device supports VK_EXT_extended_dynamic_state3.
bool IsExtExtendedDynamicState3Supported() const {
return ext_extended_dynamic_state_3;
}
/// Returns true if the device supports VK_EXT_extended_dynamic_state3.
bool IsExtExtendedDynamicState3BlendingSupported() const {
return ext_extended_dynamic_state_3_blend;
}
/// Returns true if the device supports VK_EXT_extended_dynamic_state3.
bool IsExtExtendedDynamicState3EnablesSupported() const {
return ext_extended_dynamic_state_3_enables;
}
/// Returns true if the device supports VK_EXT_line_rasterization. /// Returns true if the device supports VK_EXT_line_rasterization.
bool IsExtLineRasterizationSupported() const { bool IsExtLineRasterizationSupported() const {
return ext_line_rasterization; return ext_line_rasterization;
@ -451,6 +475,7 @@ private:
bool nv_viewport_swizzle{}; ///< Support for VK_NV_viewport_swizzle. bool nv_viewport_swizzle{}; ///< Support for VK_NV_viewport_swizzle.
bool nv_viewport_array2{}; ///< Support for VK_NV_viewport_array2. bool nv_viewport_array2{}; ///< Support for VK_NV_viewport_array2.
bool nv_geometry_shader_passthrough{}; ///< Support for VK_NV_geometry_shader_passthrough. bool nv_geometry_shader_passthrough{}; ///< Support for VK_NV_geometry_shader_passthrough.
bool khr_draw_indirect_count{}; ///< Support for VK_KHR_draw_indirect_count.
bool khr_uniform_buffer_standard_layout{}; ///< Support for scalar uniform buffer layouts. bool khr_uniform_buffer_standard_layout{}; ///< Support for scalar uniform buffer layouts.
bool khr_spirv_1_4{}; ///< Support for VK_KHR_spirv_1_4. bool khr_spirv_1_4{}; ///< Support for VK_KHR_spirv_1_4.
bool khr_workgroup_memory_explicit_layout{}; ///< Support for explicit workgroup layouts. bool khr_workgroup_memory_explicit_layout{}; ///< Support for explicit workgroup layouts.
@ -461,28 +486,33 @@ private:
bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax. bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax.
bool ext_depth_clip_control{}; ///< Support for VK_EXT_depth_clip_control bool ext_depth_clip_control{}; ///< Support for VK_EXT_depth_clip_control
bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted. bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted.
bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer. bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer.
bool ext_tooling_info{}; ///< Support for VK_EXT_tooling_info. bool ext_tooling_info{}; ///< Support for VK_EXT_tooling_info.
bool ext_subgroup_size_control{}; ///< Support for VK_EXT_subgroup_size_control. bool ext_subgroup_size_control{}; ///< Support for VK_EXT_subgroup_size_control.
bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback. bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback.
bool ext_custom_border_color{}; ///< Support for VK_EXT_custom_border_color. bool ext_custom_border_color{}; ///< Support for VK_EXT_custom_border_color.
bool ext_extended_dynamic_state{}; ///< Support for VK_EXT_extended_dynamic_state. bool ext_extended_dynamic_state{}; ///< Support for VK_EXT_extended_dynamic_state.
bool ext_line_rasterization{}; ///< Support for VK_EXT_line_rasterization. bool ext_extended_dynamic_state_2{}; ///< Support for VK_EXT_extended_dynamic_state2.
bool ext_vertex_input_dynamic_state{}; ///< Support for VK_EXT_vertex_input_dynamic_state. bool ext_extended_dynamic_state_2_extra{}; ///< Support for VK_EXT_extended_dynamic_state2.
bool ext_shader_stencil_export{}; ///< Support for VK_EXT_shader_stencil_export. bool ext_extended_dynamic_state_3{}; ///< Support for VK_EXT_extended_dynamic_state3.
bool ext_shader_atomic_int64{}; ///< Support for VK_KHR_shader_atomic_int64. bool ext_extended_dynamic_state_3_blend{}; ///< Support for VK_EXT_extended_dynamic_state3.
bool ext_conservative_rasterization{}; ///< Support for VK_EXT_conservative_rasterization. bool ext_extended_dynamic_state_3_enables{}; ///< Support for VK_EXT_extended_dynamic_state3.
bool ext_provoking_vertex{}; ///< Support for VK_EXT_provoking_vertex. bool ext_line_rasterization{}; ///< Support for VK_EXT_line_rasterization.
bool ext_memory_budget{}; ///< Support for VK_EXT_memory_budget. bool ext_vertex_input_dynamic_state{}; ///< Support for VK_EXT_vertex_input_dynamic_state.
bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config. bool ext_shader_stencil_export{}; ///< Support for VK_EXT_shader_stencil_export.
bool has_broken_cube_compatibility{}; ///< Has broken cube compatiblity bit bool ext_shader_atomic_int64{}; ///< Support for VK_KHR_shader_atomic_int64.
bool has_renderdoc{}; ///< Has RenderDoc attached bool ext_conservative_rasterization{}; ///< Support for VK_EXT_conservative_rasterization.
bool has_nsight_graphics{}; ///< Has Nsight Graphics attached bool ext_provoking_vertex{}; ///< Support for VK_EXT_provoking_vertex.
bool supports_d24_depth{}; ///< Supports D24 depth buffers. bool ext_memory_budget{}; ///< Support for VK_EXT_memory_budget.
bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting. bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config.
bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format. bool has_broken_cube_compatibility{}; ///< Has broken cube compatiblity bit
u32 max_vertex_input_attributes{}; ///< Max vertex input attributes in pipeline bool has_renderdoc{}; ///< Has RenderDoc attached
u32 max_vertex_input_bindings{}; ///< Max vertex input buffers in pipeline bool has_nsight_graphics{}; ///< Has Nsight Graphics attached
bool supports_d24_depth{}; ///< Supports D24 depth buffers.
bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting.
bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format.
u32 max_vertex_input_attributes{}; ///< Max vertex input attributes in pipeline
u32 max_vertex_input_bindings{}; ///< Max vertex input buffers in pipeline
// Telemetry parameters // Telemetry parameters
std::string vendor_name; ///< Device's driver name. std::string vendor_name; ///< Device's driver name.

View file

@ -94,6 +94,10 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
X(vkCmdDispatch); X(vkCmdDispatch);
X(vkCmdDraw); X(vkCmdDraw);
X(vkCmdDrawIndexed); X(vkCmdDrawIndexed);
X(vkCmdDrawIndirect);
X(vkCmdDrawIndexedIndirect);
X(vkCmdDrawIndirectCountKHR);
X(vkCmdDrawIndexedIndirectCountKHR);
X(vkCmdEndQuery); X(vkCmdEndQuery);
X(vkCmdEndRenderPass); X(vkCmdEndRenderPass);
X(vkCmdEndTransformFeedbackEXT); X(vkCmdEndTransformFeedbackEXT);
@ -118,12 +122,22 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
X(vkCmdSetDepthCompareOpEXT); X(vkCmdSetDepthCompareOpEXT);
X(vkCmdSetDepthTestEnableEXT); X(vkCmdSetDepthTestEnableEXT);
X(vkCmdSetDepthWriteEnableEXT); X(vkCmdSetDepthWriteEnableEXT);
X(vkCmdSetPrimitiveRestartEnableEXT);
X(vkCmdSetRasterizerDiscardEnableEXT);
X(vkCmdSetDepthBiasEnableEXT);
X(vkCmdSetLogicOpEnableEXT);
X(vkCmdSetDepthClampEnableEXT);
X(vkCmdSetFrontFaceEXT); X(vkCmdSetFrontFaceEXT);
X(vkCmdSetLogicOpEXT);
X(vkCmdSetPatchControlPointsEXT);
X(vkCmdSetLineWidth); X(vkCmdSetLineWidth);
X(vkCmdSetPrimitiveTopologyEXT); X(vkCmdSetPrimitiveTopologyEXT);
X(vkCmdSetStencilOpEXT); X(vkCmdSetStencilOpEXT);
X(vkCmdSetStencilTestEnableEXT); X(vkCmdSetStencilTestEnableEXT);
X(vkCmdSetVertexInputEXT); X(vkCmdSetVertexInputEXT);
X(vkCmdSetColorWriteMaskEXT);
X(vkCmdSetColorBlendEnableEXT);
X(vkCmdSetColorBlendEquationEXT);
X(vkCmdResolveImage); X(vkCmdResolveImage);
X(vkCreateBuffer); X(vkCreateBuffer);
X(vkCreateBufferView); X(vkCreateBufferView);

View file

@ -213,6 +213,10 @@ struct DeviceDispatch : InstanceDispatch {
PFN_vkCmdDispatch vkCmdDispatch{}; PFN_vkCmdDispatch vkCmdDispatch{};
PFN_vkCmdDraw vkCmdDraw{}; PFN_vkCmdDraw vkCmdDraw{};
PFN_vkCmdDrawIndexed vkCmdDrawIndexed{}; PFN_vkCmdDrawIndexed vkCmdDrawIndexed{};
PFN_vkCmdDrawIndirect vkCmdDrawIndirect{};
PFN_vkCmdDrawIndexedIndirect vkCmdDrawIndexedIndirect{};
PFN_vkCmdDrawIndirectCountKHR vkCmdDrawIndirectCountKHR{};
PFN_vkCmdDrawIndexedIndirectCountKHR vkCmdDrawIndexedIndirectCountKHR{};
PFN_vkCmdEndDebugUtilsLabelEXT vkCmdEndDebugUtilsLabelEXT{}; PFN_vkCmdEndDebugUtilsLabelEXT vkCmdEndDebugUtilsLabelEXT{};
PFN_vkCmdEndQuery vkCmdEndQuery{}; PFN_vkCmdEndQuery vkCmdEndQuery{};
PFN_vkCmdEndRenderPass vkCmdEndRenderPass{}; PFN_vkCmdEndRenderPass vkCmdEndRenderPass{};
@ -230,8 +234,15 @@ struct DeviceDispatch : InstanceDispatch {
PFN_vkCmdSetDepthCompareOpEXT vkCmdSetDepthCompareOpEXT{}; PFN_vkCmdSetDepthCompareOpEXT vkCmdSetDepthCompareOpEXT{};
PFN_vkCmdSetDepthTestEnableEXT vkCmdSetDepthTestEnableEXT{}; PFN_vkCmdSetDepthTestEnableEXT vkCmdSetDepthTestEnableEXT{};
PFN_vkCmdSetDepthWriteEnableEXT vkCmdSetDepthWriteEnableEXT{}; PFN_vkCmdSetDepthWriteEnableEXT vkCmdSetDepthWriteEnableEXT{};
PFN_vkCmdSetPrimitiveRestartEnableEXT vkCmdSetPrimitiveRestartEnableEXT{};
PFN_vkCmdSetRasterizerDiscardEnableEXT vkCmdSetRasterizerDiscardEnableEXT{};
PFN_vkCmdSetDepthBiasEnableEXT vkCmdSetDepthBiasEnableEXT{};
PFN_vkCmdSetLogicOpEnableEXT vkCmdSetLogicOpEnableEXT{};
PFN_vkCmdSetDepthClampEnableEXT vkCmdSetDepthClampEnableEXT{};
PFN_vkCmdSetEvent vkCmdSetEvent{}; PFN_vkCmdSetEvent vkCmdSetEvent{};
PFN_vkCmdSetFrontFaceEXT vkCmdSetFrontFaceEXT{}; PFN_vkCmdSetFrontFaceEXT vkCmdSetFrontFaceEXT{};
PFN_vkCmdSetPatchControlPointsEXT vkCmdSetPatchControlPointsEXT{};
PFN_vkCmdSetLogicOpEXT vkCmdSetLogicOpEXT{};
PFN_vkCmdSetLineWidth vkCmdSetLineWidth{}; PFN_vkCmdSetLineWidth vkCmdSetLineWidth{};
PFN_vkCmdSetPrimitiveTopologyEXT vkCmdSetPrimitiveTopologyEXT{}; PFN_vkCmdSetPrimitiveTopologyEXT vkCmdSetPrimitiveTopologyEXT{};
PFN_vkCmdSetScissor vkCmdSetScissor{}; PFN_vkCmdSetScissor vkCmdSetScissor{};
@ -242,6 +253,9 @@ struct DeviceDispatch : InstanceDispatch {
PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask{}; PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask{};
PFN_vkCmdSetVertexInputEXT vkCmdSetVertexInputEXT{}; PFN_vkCmdSetVertexInputEXT vkCmdSetVertexInputEXT{};
PFN_vkCmdSetViewport vkCmdSetViewport{}; PFN_vkCmdSetViewport vkCmdSetViewport{};
PFN_vkCmdSetColorWriteMaskEXT vkCmdSetColorWriteMaskEXT{};
PFN_vkCmdSetColorBlendEnableEXT vkCmdSetColorBlendEnableEXT{};
PFN_vkCmdSetColorBlendEquationEXT vkCmdSetColorBlendEquationEXT{};
PFN_vkCmdWaitEvents vkCmdWaitEvents{}; PFN_vkCmdWaitEvents vkCmdWaitEvents{};
PFN_vkCreateBuffer vkCreateBuffer{}; PFN_vkCreateBuffer vkCreateBuffer{};
PFN_vkCreateBufferView vkCreateBufferView{}; PFN_vkCreateBufferView vkCreateBufferView{};
@ -1019,6 +1033,29 @@ public:
first_instance); first_instance);
} }
void DrawIndirect(VkBuffer src_buffer, VkDeviceSize src_offset, u32 draw_count,
u32 stride) const noexcept {
dld->vkCmdDrawIndirect(handle, src_buffer, src_offset, draw_count, stride);
}
void DrawIndexedIndirect(VkBuffer src_buffer, VkDeviceSize src_offset, u32 draw_count,
u32 stride) const noexcept {
dld->vkCmdDrawIndexedIndirect(handle, src_buffer, src_offset, draw_count, stride);
}
void DrawIndirectCount(VkBuffer src_buffer, VkDeviceSize src_offset, VkBuffer count_buffer,
VkDeviceSize count_offset, u32 draw_count, u32 stride) const noexcept {
dld->vkCmdDrawIndirectCountKHR(handle, src_buffer, src_offset, count_buffer, count_offset,
draw_count, stride);
}
void DrawIndexedIndirectCount(VkBuffer src_buffer, VkDeviceSize src_offset,
VkBuffer count_buffer, VkDeviceSize count_offset, u32 draw_count,
u32 stride) const noexcept {
dld->vkCmdDrawIndexedIndirectCountKHR(handle, src_buffer, src_offset, count_buffer,
count_offset, draw_count, stride);
}
void ClearAttachments(Span<VkClearAttachment> attachments, void ClearAttachments(Span<VkClearAttachment> attachments,
Span<VkClearRect> rects) const noexcept { Span<VkClearRect> rects) const noexcept {
dld->vkCmdClearAttachments(handle, attachments.size(), attachments.data(), rects.size(), dld->vkCmdClearAttachments(handle, attachments.size(), attachments.data(), rects.size(),
@ -1192,10 +1229,51 @@ public:
dld->vkCmdSetDepthWriteEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); dld->vkCmdSetDepthWriteEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
} }
void SetPrimitiveRestartEnableEXT(bool enable) const noexcept {
dld->vkCmdSetPrimitiveRestartEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
}
void SetRasterizerDiscardEnableEXT(bool enable) const noexcept {
dld->vkCmdSetRasterizerDiscardEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
}
void SetDepthBiasEnableEXT(bool enable) const noexcept {
dld->vkCmdSetDepthBiasEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
}
void SetLogicOpEnableEXT(bool enable) const noexcept {
dld->vkCmdSetLogicOpEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
}
void SetDepthClampEnableEXT(bool enable) const noexcept {
dld->vkCmdSetDepthClampEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
}
void SetFrontFaceEXT(VkFrontFace front_face) const noexcept { void SetFrontFaceEXT(VkFrontFace front_face) const noexcept {
dld->vkCmdSetFrontFaceEXT(handle, front_face); dld->vkCmdSetFrontFaceEXT(handle, front_face);
} }
void SetLogicOpEXT(VkLogicOp logic_op) const noexcept {
dld->vkCmdSetLogicOpEXT(handle, logic_op);
}
void SetPatchControlPointsEXT(uint32_t patch_control_points) const noexcept {
dld->vkCmdSetPatchControlPointsEXT(handle, patch_control_points);
}
void SetColorWriteMaskEXT(u32 first, Span<VkColorComponentFlags> masks) const noexcept {
dld->vkCmdSetColorWriteMaskEXT(handle, first, masks.size(), masks.data());
}
void SetColorBlendEnableEXT(u32 first, Span<VkBool32> enables) const noexcept {
dld->vkCmdSetColorBlendEnableEXT(handle, first, enables.size(), enables.data());
}
void SetColorBlendEquationEXT(u32 first,
Span<VkColorBlendEquationEXT> equations) const noexcept {
dld->vkCmdSetColorBlendEquationEXT(handle, first, equations.size(), equations.data());
}
void SetLineWidth(float line_width) const noexcept { void SetLineWidth(float line_width) const noexcept {
dld->vkCmdSetLineWidth(handle, line_width); dld->vkCmdSetLineWidth(handle, line_width);
} }