Merge branch 'master' into vk_vfetch

This commit is contained in:
DrChat
2018-02-18 17:02:59 -06:00
37 changed files with 8433 additions and 2053 deletions

View File

@@ -26,13 +26,22 @@ void copy_128_aligned(void* dest, const void* src, size_t count) {
#if XE_ARCH_AMD64
void copy_and_swap_16_aligned(void* dest_ptr, const void* src_ptr,
size_t count) {
assert_zero(reinterpret_cast<uintptr_t>(src_ptr) & 0x1);
auto dest = reinterpret_cast<uint16_t*>(dest_ptr);
auto src = reinterpret_cast<const uint16_t*>(src_ptr);
size_t i;
for (i = 0; i + 8 <= count; i += 8) {
__m128i shufmask =
_mm_set_epi8(0x0E, 0x0F, 0x0C, 0x0D, 0x0A, 0x0B, 0x08, 0x09, 0x06, 0x07,
0x04, 0x05, 0x02, 0x03, 0x00, 0x01);
size_t i = 0;
size_t unaligned_words = (reinterpret_cast<uintptr_t>(src_ptr) & 0xF) / 2;
for (; unaligned_words > 0 && i < count; unaligned_words--, i++) {
// Copy up to 16 byte alignment.
dest[i] = byte_swap(src[i]);
}
for (; i + 8 <= count; i += 8) {
__m128i input = _mm_load_si128(reinterpret_cast<const __m128i*>(&src[i]));
__m128i output =
_mm_or_si128(_mm_slli_epi16(input, 8), _mm_srli_epi16(input, 8));
__m128i output = _mm_shuffle_epi8(input, shufmask);
_mm_store_si128(reinterpret_cast<__m128i*>(&dest[i]), output);
}
for (; i < count; ++i) { // handle residual elements
@@ -44,11 +53,14 @@ void copy_and_swap_16_unaligned(void* dest_ptr, const void* src_ptr,
size_t count) {
auto dest = reinterpret_cast<uint16_t*>(dest_ptr);
auto src = reinterpret_cast<const uint16_t*>(src_ptr);
__m128i shufmask =
_mm_set_epi8(0x0E, 0x0F, 0x0C, 0x0D, 0x0A, 0x0B, 0x08, 0x09, 0x06, 0x07,
0x04, 0x05, 0x02, 0x03, 0x00, 0x01);
size_t i;
for (i = 0; i + 8 <= count; i += 8) {
__m128i input = _mm_loadu_si128(reinterpret_cast<const __m128i*>(&src[i]));
__m128i output =
_mm_or_si128(_mm_slli_epi16(input, 8), _mm_srli_epi16(input, 8));
__m128i output = _mm_shuffle_epi8(input, shufmask);
_mm_storeu_si128(reinterpret_cast<__m128i*>(&dest[i]), output);
}
for (; i < count; ++i) { // handle residual elements
@@ -58,24 +70,22 @@ void copy_and_swap_16_unaligned(void* dest_ptr, const void* src_ptr,
void copy_and_swap_32_aligned(void* dest_ptr, const void* src_ptr,
size_t count) {
assert_zero(reinterpret_cast<uintptr_t>(src_ptr) & 0x3);
auto dest = reinterpret_cast<uint32_t*>(dest_ptr);
auto src = reinterpret_cast<const uint32_t*>(src_ptr);
__m128i byte2mask = _mm_set1_epi32(0x00FF0000);
__m128i byte3mask = _mm_set1_epi32(0x0000FF00);
size_t i;
for (i = 0; i + 4 <= count; i += 4) {
__m128i shufmask =
_mm_set_epi8(0x0C, 0x0D, 0x0E, 0x0F, 0x08, 0x09, 0x0A, 0x0B, 0x04, 0x05,
0x06, 0x07, 0x00, 0x01, 0x02, 0x03);
size_t i = 0;
size_t unaligned_dwords = (reinterpret_cast<uintptr_t>(src_ptr) & 0xF) / 4;
for (; unaligned_dwords > 0 && i < count; unaligned_dwords--, i++) {
// Copy up to 16 byte alignment.
dest[i] = byte_swap(src[i]);
}
for (; i + 4 <= count; i += 4) {
__m128i input = _mm_load_si128(reinterpret_cast<const __m128i*>(&src[i]));
// Do the four shifts.
__m128i byte1 = _mm_slli_epi32(input, 24);
__m128i byte2 = _mm_slli_epi32(input, 8);
__m128i byte3 = _mm_srli_epi32(input, 8);
__m128i byte4 = _mm_srli_epi32(input, 24);
// OR bytes together.
__m128i output = _mm_or_si128(byte1, byte4);
byte2 = _mm_and_si128(byte2, byte2mask);
output = _mm_or_si128(output, byte2);
byte3 = _mm_and_si128(byte3, byte3mask);
output = _mm_or_si128(output, byte3);
__m128i output = _mm_shuffle_epi8(input, shufmask);
_mm_store_si128(reinterpret_cast<__m128i*>(&dest[i]), output);
}
for (; i < count; ++i) { // handle residual elements
@@ -87,22 +97,14 @@ void copy_and_swap_32_unaligned(void* dest_ptr, const void* src_ptr,
size_t count) {
auto dest = reinterpret_cast<uint32_t*>(dest_ptr);
auto src = reinterpret_cast<const uint32_t*>(src_ptr);
__m128i byte2mask = _mm_set1_epi32(0x00FF0000);
__m128i byte3mask = _mm_set1_epi32(0x0000FF00);
__m128i shufmask =
_mm_set_epi8(0x0C, 0x0D, 0x0E, 0x0F, 0x08, 0x09, 0x0A, 0x0B, 0x04, 0x05,
0x06, 0x07, 0x00, 0x01, 0x02, 0x03);
size_t i;
for (i = 0; i + 4 <= count; i += 4) {
__m128i input = _mm_loadu_si128(reinterpret_cast<const __m128i*>(&src[i]));
// Do the four shifts.
__m128i byte1 = _mm_slli_epi32(input, 24);
__m128i byte2 = _mm_slli_epi32(input, 8);
__m128i byte3 = _mm_srli_epi32(input, 8);
__m128i byte4 = _mm_srli_epi32(input, 24);
// OR bytes together.
__m128i output = _mm_or_si128(byte1, byte4);
byte2 = _mm_and_si128(byte2, byte2mask);
output = _mm_or_si128(output, byte2);
byte3 = _mm_and_si128(byte3, byte3mask);
output = _mm_or_si128(output, byte3);
__m128i output = _mm_shuffle_epi8(input, shufmask);
_mm_storeu_si128(reinterpret_cast<__m128i*>(&dest[i]), output);
}
for (; i < count; ++i) { // handle residual elements
@@ -112,26 +114,22 @@ void copy_and_swap_32_unaligned(void* dest_ptr, const void* src_ptr,
void copy_and_swap_64_aligned(void* dest_ptr, const void* src_ptr,
size_t count) {
assert_zero(reinterpret_cast<uintptr_t>(src_ptr) & 0x7);
auto dest = reinterpret_cast<uint64_t*>(dest_ptr);
auto src = reinterpret_cast<const uint64_t*>(src_ptr);
__m128i byte2mask = _mm_set1_epi32(0x00FF0000);
__m128i byte3mask = _mm_set1_epi32(0x0000FF00);
size_t i;
for (i = 0; i + 2 <= count; i += 2) {
__m128i shufmask =
_mm_set_epi8(0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00, 0x01,
0x02, 0x03, 0x04, 0x05, 0x06, 0x07);
size_t i = 0;
size_t unaligned_qwords = (reinterpret_cast<uintptr_t>(src_ptr) & 0xF) / 8;
for (; unaligned_qwords > 0 && i < count; unaligned_qwords--, i++) {
// Copy up to 16 byte alignment.
dest[i] = byte_swap(src[i]);
}
for (; i + 2 <= count; i += 2) {
__m128i input = _mm_load_si128(reinterpret_cast<const __m128i*>(&src[i]));
// Do the four shifts.
__m128i byte1 = _mm_slli_epi32(input, 24);
__m128i byte2 = _mm_slli_epi32(input, 8);
__m128i byte3 = _mm_srli_epi32(input, 8);
__m128i byte4 = _mm_srli_epi32(input, 24);
// OR bytes together.
__m128i output = _mm_or_si128(byte1, byte4);
byte2 = _mm_and_si128(byte2, byte2mask);
output = _mm_or_si128(output, byte2);
byte3 = _mm_and_si128(byte3, byte3mask);
output = _mm_or_si128(output, byte3);
// Reorder the two words.
output = _mm_shuffle_epi32(output, _MM_SHUFFLE(2, 3, 0, 1));
__m128i output = _mm_shuffle_epi8(input, shufmask);
_mm_store_si128(reinterpret_cast<__m128i*>(&dest[i]), output);
}
for (; i < count; ++i) { // handle residual elements
@@ -143,24 +141,14 @@ void copy_and_swap_64_unaligned(void* dest_ptr, const void* src_ptr,
size_t count) {
auto dest = reinterpret_cast<uint64_t*>(dest_ptr);
auto src = reinterpret_cast<const uint64_t*>(src_ptr);
__m128i byte2mask = _mm_set1_epi32(0x00FF0000);
__m128i byte3mask = _mm_set1_epi32(0x0000FF00);
__m128i shufmask =
_mm_set_epi8(0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00, 0x01,
0x02, 0x03, 0x04, 0x05, 0x06, 0x07);
size_t i;
for (i = 0; i + 2 <= count; i += 2) {
__m128i input = _mm_loadu_si128(reinterpret_cast<const __m128i*>(&src[i]));
// Do the four shifts.
__m128i byte1 = _mm_slli_epi32(input, 24);
__m128i byte2 = _mm_slli_epi32(input, 8);
__m128i byte3 = _mm_srli_epi32(input, 8);
__m128i byte4 = _mm_srli_epi32(input, 24);
// OR bytes together.
__m128i output = _mm_or_si128(byte1, byte4);
byte2 = _mm_and_si128(byte2, byte2mask);
output = _mm_or_si128(output, byte2);
byte3 = _mm_and_si128(byte3, byte3mask);
output = _mm_or_si128(output, byte3);
// Reorder the two words.
output = _mm_shuffle_epi32(output, _MM_SHUFFLE(2, 3, 0, 1));
__m128i output = _mm_shuffle_epi8(input, shufmask);
_mm_storeu_si128(reinterpret_cast<__m128i*>(&dest[i]), output);
}
for (; i < count; ++i) { // handle residual elements

View File

@@ -12,6 +12,7 @@
#include <cstdint>
#include <string>
#include <type_traits>
#include <vector>
#include "xenia/base/assert.h"
@@ -72,16 +73,25 @@ class RingBuffer {
}
template <typename T>
T Read(bool swap = false) {
static_assert(sizeof(T) <= 8, "Immediate read only supports basic types!");
T Read() {
static_assert(std::is_fundamental<T>::value,
"Immediate read only supports basic types!");
T imm;
size_t read = Read(reinterpret_cast<uint8_t*>(&imm), sizeof(T));
assert_true(read == sizeof(T));
if (swap) {
imm = xe::byte_swap(imm);
}
return imm;
}
template <typename T>
T ReadAndSwap() {
static_assert(std::is_fundamental<T>::value,
"Immediate read only supports basic types!");
T imm;
size_t read = Read(reinterpret_cast<uint8_t*>(&imm), sizeof(T));
assert_true(read == sizeof(T));
imm = xe::byte_swap(imm);
return imm;
}

View File

@@ -6484,24 +6484,17 @@ struct CNTLZ_I8 : Sequence<CNTLZ_I8, I<OPCODE_CNTLZ, I8Op, I8Op>> {
e.lzcnt(i.dest.reg().cvt16(), i.dest.reg().cvt16());
e.sub(i.dest, 8);
} else {
Xbyak::Label jz, jend;
Xbyak::Label end;
e.inLocalLabel();
// BSR: searches $2 until MSB 1 found, stores idx (from bit 0) in $1
// if input is 0, results are undefined (and ZF is set)
e.bsr(i.dest, i.src1);
e.jz(jz); // Jump if zero
e.bsr(e.rax, i.src1); // ZF set if i.src1 is 0
e.mov(i.dest, 0x8);
e.jz(end);
// Invert the result (7 - i.dest)
e.xor_(i.dest, 0x7);
e.jmp(jend); // Jmp to end
e.xor_(e.rax, 0x7);
e.mov(i.dest, e.rax);
// src1 was zero, so write 8 to the dest reg
e.L(jz);
e.mov(i.dest, 8);
e.L(jend);
e.L(end);
e.outLocalLabel();
}
}
@@ -6512,24 +6505,17 @@ struct CNTLZ_I16 : Sequence<CNTLZ_I16, I<OPCODE_CNTLZ, I8Op, I16Op>> {
// LZCNT: searches $2 until MSB 1 found, stores idx (from last bit) in $1
e.lzcnt(i.dest.reg().cvt32(), i.src1);
} else {
Xbyak::Label jz, jend;
Xbyak::Label end;
e.inLocalLabel();
// BSR: searches $2 until MSB 1 found, stores idx (from bit 0) in $1
// if input is 0, results are undefined (and ZF is set)
e.bsr(i.dest, i.src1);
e.jz(jz); // Jump if zero
e.bsr(e.rax, i.src1); // ZF set if i.src1 is 0
e.mov(i.dest, 0x10);
e.jz(end);
// Invert the result (15 - i.dest)
e.xor_(i.dest, 0xF);
e.jmp(jend); // Jmp to end
e.xor_(e.rax, 0x0F);
e.mov(i.dest, e.rax);
// src1 was zero, so write 16 to the dest reg
e.L(jz);
e.mov(i.dest, 16);
e.L(jend);
e.L(end);
e.outLocalLabel();
}
}
@@ -6539,24 +6525,17 @@ struct CNTLZ_I32 : Sequence<CNTLZ_I32, I<OPCODE_CNTLZ, I8Op, I32Op>> {
if (e.IsFeatureEnabled(kX64EmitLZCNT)) {
e.lzcnt(i.dest.reg().cvt32(), i.src1);
} else {
Xbyak::Label jz, jend;
Xbyak::Label end;
e.inLocalLabel();
// BSR: searches $2 until MSB 1 found, stores idx (from bit 0) in $1
// if input is 0, results are undefined (and ZF is set)
e.bsr(i.dest, i.src1);
e.jz(jz); // Jump if zero
e.bsr(e.rax, i.src1); // ZF set if i.src1 is 0
e.mov(i.dest, 0x20);
e.jz(end);
// Invert the result (31 - i.dest)
e.xor_(i.dest, 0x1F);
e.jmp(jend); // Jmp to end
e.xor_(e.rax, 0x1F);
e.mov(i.dest, e.rax);
// src1 was zero, so write 32 to the dest reg
e.L(jz);
e.mov(i.dest, 32);
e.L(jend);
e.L(end);
e.outLocalLabel();
}
}
@@ -6566,24 +6545,17 @@ struct CNTLZ_I64 : Sequence<CNTLZ_I64, I<OPCODE_CNTLZ, I8Op, I64Op>> {
if (e.IsFeatureEnabled(kX64EmitLZCNT)) {
e.lzcnt(i.dest.reg().cvt64(), i.src1);
} else {
Xbyak::Label jz, jend;
Xbyak::Label end;
e.inLocalLabel();
// BSR: searches $2 until MSB 1 found, stores idx (from bit 0) in $1
// if input is 0, results are undefined (and ZF is set)
e.bsr(i.dest, i.src1);
e.jz(jz); // Jump if zero
e.bsr(e.rax, i.src1); // ZF set if i.src1 is 0
e.mov(i.dest, 0x40);
e.jz(end);
// Invert the result (63 - i.dest)
e.xor_(i.dest, 0x3F);
e.jmp(jend); // Jmp to end
e.xor_(e.rax, 0x3F);
e.mov(i.dest, e.rax);
// src1 was zero, so write 64 to the dest reg
e.L(jz);
e.mov(i.dest, 64);
e.L(jend);
e.L(end);
e.outLocalLabel();
}
}

View File

@@ -240,12 +240,20 @@ bool MMIOHandler::IsRangeWatched(uint32_t physical_address, size_t length) {
for (auto it = access_watches_.begin(); it != access_watches_.end(); ++it) {
auto entry = *it;
if ((entry->address <= physical_address &&
entry->address + entry->length > physical_address) ||
(entry->address >= physical_address &&
entry->address < physical_address + length)) {
// This watch lies within the range.
entry->address + entry->length > physical_address + length)) {
// This range lies entirely within this watch.
return true;
}
// TODO(DrChat): Check if the range is partially covered, and subtract the
// covered portion if it is.
if ((entry->address <= physical_address &&
entry->address + entry->length > physical_address)) {
// The beginning of range lies partially within this watch.
} else if ((entry->address < physical_address + length &&
entry->address + entry->length > physical_address + length)) {
// The ending of this range lies partially within this watch.
}
}
return false;

View File

@@ -77,7 +77,7 @@ class MMIOHandler {
// Fires and clears any access watches that overlap this range.
void InvalidateRange(uint32_t physical_address, size_t length);
// Returns true if /any/ part of this range is watched.
// Returns true if /all/ of this range is watched.
bool IsRangeWatched(uint32_t physical_address, size_t length);
protected:

View File

@@ -960,7 +960,7 @@ int InstrEmit_rlwimix(PPCHIRBuilder& f, const InstrData& i) {
// RA <- r&m | (RA)&¬m
Value* v = f.LoadGPR(i.M.RT);
// (x||x)
v = f.Or(f.Shl(v, 32), f.And(v, f.LoadConstantUint64(0xFFFFFFFF)));
v = f.Or(f.Shl(v, 32), f.ZeroExtend(f.Truncate(v, INT32_TYPE), INT64_TYPE));
if (i.M.SH) {
v = f.RotateLeft(v, f.LoadConstantInt8(i.M.SH));
}
@@ -1018,7 +1018,7 @@ int InstrEmit_rlwnmx(PPCHIRBuilder& f, const InstrData& i) {
f.And(f.Truncate(f.LoadGPR(i.M.SH), INT8_TYPE), f.LoadConstantInt8(0x1F));
Value* v = f.LoadGPR(i.M.RT);
// (x||x)
v = f.Or(f.Shl(v, 32), f.And(v, f.LoadConstantUint64(0xFFFFFFFF)));
v = f.Or(f.Shl(v, 32), f.ZeroExtend(f.Truncate(v, INT32_TYPE), INT64_TYPE));
v = f.RotateLeft(v, sh);
v = f.And(v, f.LoadConstantUint64(XEMASK(i.M.MB + 32, i.M.ME + 32)));
f.StoreGPR(i.M.RA, v);

View File

@@ -10,6 +10,7 @@
#include "xenia/gpu/command_processor.h"
#include <algorithm>
#include <cinttypes>
#include <cmath>
#include "xenia/base/byte_stream.h"
@@ -19,6 +20,7 @@
#include "xenia/base/ring_buffer.h"
#include "xenia/gpu/gpu_flags.h"
#include "xenia/gpu/graphics_system.h"
#include "xenia/gpu/registers.h"
#include "xenia/gpu/sampler_info.h"
#include "xenia/gpu/texture_info.h"
#include "xenia/gpu/xenos.h"
@@ -166,6 +168,9 @@ void CommandProcessor::WorkerThreadMain() {
xe::store_and_swap<uint32_t>(
memory_->TranslatePhysical(read_ptr_writeback_ptr_), read_ptr_index_);
}
// FIXME: We're supposed to process the WAIT_UNTIL register at this point,
// but no games seem to actually use it.
}
ShutdownContext();
@@ -438,7 +443,7 @@ void CommandProcessor::ExecutePacket(uint32_t ptr, uint32_t count) {
}
bool CommandProcessor::ExecutePacket(RingBuffer* reader) {
const uint32_t packet = reader->Read<uint32_t>(true);
const uint32_t packet = reader->ReadAndSwap<uint32_t>();
const uint32_t packet_type = packet >> 30;
if (packet == 0) {
trace_writer_.WritePacketStart(uint32_t(reader->read_ptr() - 4), 1);
@@ -478,7 +483,7 @@ bool CommandProcessor::ExecutePacketType0(RingBuffer* reader, uint32_t packet) {
uint32_t base_index = (packet & 0x7FFF);
uint32_t write_one_reg = (packet >> 15) & 0x1;
for (uint32_t m = 0; m < count; m++) {
uint32_t reg_data = reader->Read<uint32_t>(true);
uint32_t reg_data = reader->ReadAndSwap<uint32_t>();
uint32_t target_index = write_one_reg ? base_index : base_index + m;
WriteRegister(target_index, reg_data);
}
@@ -493,8 +498,8 @@ bool CommandProcessor::ExecutePacketType1(RingBuffer* reader, uint32_t packet) {
trace_writer_.WritePacketStart(uint32_t(reader->read_ptr() - 4), 3);
uint32_t reg_index_1 = packet & 0x7FF;
uint32_t reg_index_2 = (packet >> 11) & 0x7FF;
uint32_t reg_data_1 = reader->Read<uint32_t>(true);
uint32_t reg_data_2 = reader->Read<uint32_t>(true);
uint32_t reg_data_1 = reader->ReadAndSwap<uint32_t>();
uint32_t reg_data_2 = reader->ReadAndSwap<uint32_t>();
WriteRegister(reg_index_1, reg_data_1);
WriteRegister(reg_index_2, reg_data_2);
trace_writer_.WritePacketEnd();
@@ -617,38 +622,38 @@ bool CommandProcessor::ExecutePacketType3(RingBuffer* reader, uint32_t packet) {
break;
case PM4_SET_BIN_MASK_LO: {
uint32_t value = reader->Read<uint32_t>(true);
uint32_t value = reader->ReadAndSwap<uint32_t>();
bin_mask_ = (bin_mask_ & 0xFFFFFFFF00000000ull) | value;
result = true;
} break;
case PM4_SET_BIN_MASK_HI: {
uint32_t value = reader->Read<uint32_t>(true);
uint32_t value = reader->ReadAndSwap<uint32_t>();
bin_mask_ =
(bin_mask_ & 0xFFFFFFFFull) | (static_cast<uint64_t>(value) << 32);
result = true;
} break;
case PM4_SET_BIN_SELECT_LO: {
uint32_t value = reader->Read<uint32_t>(true);
uint32_t value = reader->ReadAndSwap<uint32_t>();
bin_select_ = (bin_select_ & 0xFFFFFFFF00000000ull) | value;
result = true;
} break;
case PM4_SET_BIN_SELECT_HI: {
uint32_t value = reader->Read<uint32_t>(true);
uint32_t value = reader->ReadAndSwap<uint32_t>();
bin_select_ =
(bin_select_ & 0xFFFFFFFFull) | (static_cast<uint64_t>(value) << 32);
result = true;
} break;
case PM4_SET_BIN_MASK: {
assert_true(count == 2);
uint64_t val_hi = reader->Read<uint32_t>(true);
uint64_t val_lo = reader->Read<uint32_t>(true);
uint64_t val_hi = reader->ReadAndSwap<uint32_t>();
uint64_t val_lo = reader->ReadAndSwap<uint32_t>();
bin_mask_ = (val_hi << 32) | val_lo;
result = true;
} break;
case PM4_SET_BIN_SELECT: {
assert_true(count == 2);
uint64_t val_hi = reader->Read<uint32_t>(true);
uint64_t val_lo = reader->Read<uint32_t>(true);
uint64_t val_hi = reader->ReadAndSwap<uint32_t>();
uint64_t val_lo = reader->ReadAndSwap<uint32_t>();
bin_select_ = (val_hi << 32) | val_lo;
result = true;
} break;
@@ -708,7 +713,7 @@ bool CommandProcessor::ExecutePacketType3_INTERRUPT(RingBuffer* reader,
SCOPE_profile_cpu_f("gpu");
// generate interrupt from the command stream
uint32_t cpu_mask = reader->Read<uint32_t>(true);
uint32_t cpu_mask = reader->ReadAndSwap<uint32_t>();
for (int n = 0; n < 6; n++) {
if (cpu_mask & (1 << n)) {
graphics_system_->DispatchInterruptCallback(1, n);
@@ -730,13 +735,13 @@ bool CommandProcessor::ExecutePacketType3_XE_SWAP(RingBuffer* reader,
// VdSwap will post this to tell us we need to swap the screen/fire an
// interrupt.
// 63 words here, but only the first has any data.
uint32_t magic = reader->Read<uint32_t>(true);
uint32_t magic = reader->ReadAndSwap<uint32_t>();
assert_true(magic == 'SWAP');
// TODO(benvanik): only swap frontbuffer ptr.
uint32_t frontbuffer_ptr = reader->Read<uint32_t>(true);
uint32_t frontbuffer_width = reader->Read<uint32_t>(true);
uint32_t frontbuffer_height = reader->Read<uint32_t>(true);
uint32_t frontbuffer_ptr = reader->ReadAndSwap<uint32_t>();
uint32_t frontbuffer_width = reader->ReadAndSwap<uint32_t>();
uint32_t frontbuffer_height = reader->ReadAndSwap<uint32_t>();
reader->AdvanceRead((count - 4) * sizeof(uint32_t));
if (swap_mode_ == SwapMode::kNormal) {
@@ -751,8 +756,8 @@ bool CommandProcessor::ExecutePacketType3_INDIRECT_BUFFER(RingBuffer* reader,
uint32_t packet,
uint32_t count) {
// indirect buffer dispatch
uint32_t list_ptr = CpuToGpu(reader->Read<uint32_t>(true));
uint32_t list_length = reader->Read<uint32_t>(true);
uint32_t list_ptr = CpuToGpu(reader->ReadAndSwap<uint32_t>());
uint32_t list_length = reader->ReadAndSwap<uint32_t>();
assert_zero(list_length & ~0xFFFFF);
list_length &= 0xFFFFF;
ExecuteIndirectBuffer(GpuToCpu(list_ptr), list_length);
@@ -765,11 +770,11 @@ bool CommandProcessor::ExecutePacketType3_WAIT_REG_MEM(RingBuffer* reader,
SCOPE_profile_cpu_f("gpu");
// wait until a register or memory location is a specific value
uint32_t wait_info = reader->Read<uint32_t>(true);
uint32_t poll_reg_addr = reader->Read<uint32_t>(true);
uint32_t ref = reader->Read<uint32_t>(true);
uint32_t mask = reader->Read<uint32_t>(true);
uint32_t wait = reader->Read<uint32_t>(true);
uint32_t wait_info = reader->ReadAndSwap<uint32_t>();
uint32_t poll_reg_addr = reader->ReadAndSwap<uint32_t>();
uint32_t ref = reader->ReadAndSwap<uint32_t>();
uint32_t mask = reader->ReadAndSwap<uint32_t>();
uint32_t wait = reader->ReadAndSwap<uint32_t>();
bool matched = false;
do {
uint32_t value;
@@ -846,9 +851,9 @@ bool CommandProcessor::ExecutePacketType3_REG_RMW(RingBuffer* reader,
uint32_t count) {
// register read/modify/write
// ? (used during shader upload and edram setup)
uint32_t rmw_info = reader->Read<uint32_t>(true);
uint32_t and_mask = reader->Read<uint32_t>(true);
uint32_t or_mask = reader->Read<uint32_t>(true);
uint32_t rmw_info = reader->ReadAndSwap<uint32_t>();
uint32_t and_mask = reader->ReadAndSwap<uint32_t>();
uint32_t or_mask = reader->ReadAndSwap<uint32_t>();
uint32_t value = register_file_->values[rmw_info & 0x1FFF].u32;
if ((rmw_info >> 31) & 0x1) {
// & reg
@@ -874,8 +879,8 @@ bool CommandProcessor::ExecutePacketType3_REG_TO_MEM(RingBuffer* reader,
// Copy Register to Memory (?)
// Count is 2, assuming a Register Addr and a Memory Addr.
uint32_t reg_addr = reader->Read<uint32_t>(true);
uint32_t mem_addr = reader->Read<uint32_t>(true);
uint32_t reg_addr = reader->ReadAndSwap<uint32_t>();
uint32_t mem_addr = reader->ReadAndSwap<uint32_t>();
uint32_t reg_val;
@@ -894,9 +899,9 @@ bool CommandProcessor::ExecutePacketType3_REG_TO_MEM(RingBuffer* reader,
bool CommandProcessor::ExecutePacketType3_MEM_WRITE(RingBuffer* reader,
uint32_t packet,
uint32_t count) {
uint32_t write_addr = reader->Read<uint32_t>(true);
uint32_t write_addr = reader->ReadAndSwap<uint32_t>();
for (uint32_t i = 0; i < count - 1; i++) {
uint32_t write_data = reader->Read<uint32_t>(true);
uint32_t write_data = reader->ReadAndSwap<uint32_t>();
auto endianness = static_cast<Endian>(write_addr & 0x3);
auto addr = write_addr & ~0x3;
@@ -913,12 +918,12 @@ bool CommandProcessor::ExecutePacketType3_COND_WRITE(RingBuffer* reader,
uint32_t packet,
uint32_t count) {
// conditional write to memory or register
uint32_t wait_info = reader->Read<uint32_t>(true);
uint32_t poll_reg_addr = reader->Read<uint32_t>(true);
uint32_t ref = reader->Read<uint32_t>(true);
uint32_t mask = reader->Read<uint32_t>(true);
uint32_t write_reg_addr = reader->Read<uint32_t>(true);
uint32_t write_data = reader->Read<uint32_t>(true);
uint32_t wait_info = reader->ReadAndSwap<uint32_t>();
uint32_t poll_reg_addr = reader->ReadAndSwap<uint32_t>();
uint32_t ref = reader->ReadAndSwap<uint32_t>();
uint32_t mask = reader->ReadAndSwap<uint32_t>();
uint32_t write_reg_addr = reader->ReadAndSwap<uint32_t>();
uint32_t write_data = reader->ReadAndSwap<uint32_t>();
uint32_t value;
if (wait_info & 0x10) {
// Memory.
@@ -980,7 +985,7 @@ bool CommandProcessor::ExecutePacketType3_EVENT_WRITE(RingBuffer* reader,
uint32_t packet,
uint32_t count) {
// generate an event that creates a write to memory when completed
uint32_t initiator = reader->Read<uint32_t>(true);
uint32_t initiator = reader->ReadAndSwap<uint32_t>();
// Writeback initiator.
WriteRegister(XE_GPU_REG_VGT_EVENT_INITIATOR, initiator & 0x3F);
if (count == 1) {
@@ -997,9 +1002,9 @@ bool CommandProcessor::ExecutePacketType3_EVENT_WRITE_SHD(RingBuffer* reader,
uint32_t packet,
uint32_t count) {
// generate a VS|PS_done event
uint32_t initiator = reader->Read<uint32_t>(true);
uint32_t address = reader->Read<uint32_t>(true);
uint32_t value = reader->Read<uint32_t>(true);
uint32_t initiator = reader->ReadAndSwap<uint32_t>();
uint32_t address = reader->ReadAndSwap<uint32_t>();
uint32_t value = reader->ReadAndSwap<uint32_t>();
// Writeback initiator.
WriteRegister(XE_GPU_REG_VGT_EVENT_INITIATOR, initiator & 0x3F);
uint32_t data_value;
@@ -1022,13 +1027,17 @@ bool CommandProcessor::ExecutePacketType3_EVENT_WRITE_EXT(RingBuffer* reader,
uint32_t packet,
uint32_t count) {
// generate a screen extent event
uint32_t initiator = reader->Read<uint32_t>(true);
uint32_t address = reader->Read<uint32_t>(true);
uint32_t initiator = reader->ReadAndSwap<uint32_t>();
uint32_t address = reader->ReadAndSwap<uint32_t>();
// Writeback initiator.
WriteRegister(XE_GPU_REG_VGT_EVENT_INITIATOR, initiator & 0x3F);
auto endianness = static_cast<Endian>(address & 0x3);
address &= ~0x3;
// Let us hope we can fake this.
// This callback tells the driver the xy coordinates affected by a previous
// drawcall.
// https://www.google.com/patents/US20060055701
uint16_t extents[] = {
0 >> 3, // min x
2560 >> 3, // max x
@@ -1048,7 +1057,7 @@ bool CommandProcessor::ExecutePacketType3_EVENT_WRITE_ZPD(RingBuffer* reader,
uint32_t packet,
uint32_t count) {
assert_true(count == 1);
uint32_t initiator = reader->Read<uint32_t>(true);
uint32_t initiator = reader->ReadAndSwap<uint32_t>();
// Writeback initiator.
WriteRegister(XE_GPU_REG_VGT_EVENT_INITIATOR, initiator & 0x3F);
@@ -1065,8 +1074,10 @@ bool CommandProcessor::ExecutePacketType3_DRAW_INDX(RingBuffer* reader,
// initiate fetch of index buffer and draw
// if dword0 != 0, this is a conditional draw based on viz query.
// This ID matches the one issued in PM4_VIZ_QUERY
uint32_t dword0 = reader->Read<uint32_t>(true); // viz query info
uint32_t dword1 = reader->Read<uint32_t>(true);
// ID = dword0 & 0x3F;
// use = dword0 & 0x40;
uint32_t dword0 = reader->ReadAndSwap<uint32_t>(); // viz query info
uint32_t dword1 = reader->ReadAndSwap<uint32_t>();
uint32_t index_count = dword1 >> 16;
auto prim_type = static_cast<PrimitiveType>(dword1 & 0x3F);
bool is_indexed = false;
@@ -1076,8 +1087,8 @@ bool CommandProcessor::ExecutePacketType3_DRAW_INDX(RingBuffer* reader,
// DI_SRC_SEL_DMA
// Indexed draw.
is_indexed = true;
index_buffer_info.guest_base = reader->Read<uint32_t>(true);
uint32_t index_size = reader->Read<uint32_t>(true);
index_buffer_info.guest_base = reader->ReadAndSwap<uint32_t>();
uint32_t index_size = reader->ReadAndSwap<uint32_t>();
index_buffer_info.endianness = static_cast<Endian>(index_size >> 30);
index_size &= 0x00FFFFFF;
bool index_32bit = (dword1 >> 11) & 0x1;
@@ -1113,7 +1124,7 @@ bool CommandProcessor::ExecutePacketType3_DRAW_INDX_2(RingBuffer* reader,
uint32_t packet,
uint32_t count) {
// draw using supplied indices in packet
uint32_t dword0 = reader->Read<uint32_t>(true);
uint32_t dword0 = reader->ReadAndSwap<uint32_t>();
uint32_t index_count = dword0 >> 16;
auto prim_type = static_cast<PrimitiveType>(dword0 & 0x3F);
uint32_t src_sel = (dword0 >> 6) & 0x3;
@@ -1139,7 +1150,7 @@ bool CommandProcessor::ExecutePacketType3_SET_CONSTANT(RingBuffer* reader,
// load constant into chip and to memory
// PM4_REG(reg) ((0x4 << 16) | (GSL_HAL_SUBBLOCK_OFFSET(reg)))
// reg - 0x2000
uint32_t offset_type = reader->Read<uint32_t>(true);
uint32_t offset_type = reader->ReadAndSwap<uint32_t>();
uint32_t index = offset_type & 0x7FF;
uint32_t type = (offset_type >> 16) & 0xFF;
switch (type) {
@@ -1164,7 +1175,7 @@ bool CommandProcessor::ExecutePacketType3_SET_CONSTANT(RingBuffer* reader,
return true;
}
for (uint32_t n = 0; n < count - 1; n++, index++) {
uint32_t data = reader->Read<uint32_t>(true);
uint32_t data = reader->ReadAndSwap<uint32_t>();
WriteRegister(index, data);
}
return true;
@@ -1173,10 +1184,10 @@ bool CommandProcessor::ExecutePacketType3_SET_CONSTANT(RingBuffer* reader,
bool CommandProcessor::ExecutePacketType3_SET_CONSTANT2(RingBuffer* reader,
uint32_t packet,
uint32_t count) {
uint32_t offset_type = reader->Read<uint32_t>(true);
uint32_t offset_type = reader->ReadAndSwap<uint32_t>();
uint32_t index = offset_type & 0xFFFF;
for (uint32_t n = 0; n < count - 1; n++, index++) {
uint32_t data = reader->Read<uint32_t>(true);
uint32_t data = reader->ReadAndSwap<uint32_t>();
WriteRegister(index, data);
}
return true;
@@ -1186,11 +1197,11 @@ bool CommandProcessor::ExecutePacketType3_LOAD_ALU_CONSTANT(RingBuffer* reader,
uint32_t packet,
uint32_t count) {
// load constants from memory
uint32_t address = reader->Read<uint32_t>(true);
uint32_t address = reader->ReadAndSwap<uint32_t>();
address &= 0x3FFFFFFF;
uint32_t offset_type = reader->Read<uint32_t>(true);
uint32_t offset_type = reader->ReadAndSwap<uint32_t>();
uint32_t index = offset_type & 0x7FF;
uint32_t size_dwords = reader->Read<uint32_t>(true);
uint32_t size_dwords = reader->ReadAndSwap<uint32_t>();
size_dwords &= 0xFFF;
uint32_t type = (offset_type >> 16) & 0xFF;
switch (type) {
@@ -1224,10 +1235,10 @@ bool CommandProcessor::ExecutePacketType3_LOAD_ALU_CONSTANT(RingBuffer* reader,
bool CommandProcessor::ExecutePacketType3_SET_SHADER_CONSTANTS(
RingBuffer* reader, uint32_t packet, uint32_t count) {
uint32_t offset_type = reader->Read<uint32_t>(true);
uint32_t offset_type = reader->ReadAndSwap<uint32_t>();
uint32_t index = offset_type & 0xFFFF;
for (uint32_t n = 0; n < count - 1; n++, index++) {
uint32_t data = reader->Read<uint32_t>(true);
uint32_t data = reader->ReadAndSwap<uint32_t>();
WriteRegister(index, data);
}
return true;
@@ -1239,10 +1250,10 @@ bool CommandProcessor::ExecutePacketType3_IM_LOAD(RingBuffer* reader,
SCOPE_profile_cpu_f("gpu");
// load sequencer instruction memory (pointer-based)
uint32_t addr_type = reader->Read<uint32_t>(true);
uint32_t addr_type = reader->ReadAndSwap<uint32_t>();
auto shader_type = static_cast<ShaderType>(addr_type & 0x3);
uint32_t addr = addr_type & ~0x3;
uint32_t start_size = reader->Read<uint32_t>(true);
uint32_t start_size = reader->ReadAndSwap<uint32_t>();
uint32_t start = start_size >> 16;
uint32_t size_dwords = start_size & 0xFFFF; // dwords
assert_true(start == 0);
@@ -1270,8 +1281,8 @@ bool CommandProcessor::ExecutePacketType3_IM_LOAD_IMMEDIATE(RingBuffer* reader,
SCOPE_profile_cpu_f("gpu");
// load sequencer instruction memory (code embedded in packet)
uint32_t dword0 = reader->Read<uint32_t>(true);
uint32_t dword1 = reader->Read<uint32_t>(true);
uint32_t dword0 = reader->ReadAndSwap<uint32_t>();
uint32_t dword1 = reader->ReadAndSwap<uint32_t>();
auto shader_type = static_cast<ShaderType>(dword0);
uint32_t start_size = dword1;
uint32_t start = start_size >> 16;
@@ -1301,7 +1312,7 @@ bool CommandProcessor::ExecutePacketType3_INVALIDATE_STATE(RingBuffer* reader,
uint32_t packet,
uint32_t count) {
// selective invalidation of state pointers
/*uint32_t mask =*/reader->Read<uint32_t>(true);
/*uint32_t mask =*/reader->ReadAndSwap<uint32_t>();
// driver_->InvalidateState(mask);
return true;
}
@@ -1313,12 +1324,19 @@ bool CommandProcessor::ExecutePacketType3_VIZ_QUERY(RingBuffer* reader,
// http://www.google.com/patents/US20050195186
assert_true(count == 1);
// Some sort of ID?
// This appears to reset a viz query context.
// This ID matches the ID in conditional draw commands.
// Patent says the driver sets the viz_query register with info about the
// context ID.
uint32_t dword0 = reader->Read<uint32_t>(true);
uint32_t dword0 = reader->ReadAndSwap<uint32_t>();
uint32_t id = dword0 & 0x3F;
uint32_t end = dword0 & 0x80;
if (!end) {
// begin a new viz query @ id
WriteRegister(XE_GPU_REG_VGT_EVENT_INITIATOR, VIZQUERY_START);
XELOGGPU("Begin viz query ID %.2X", id);
} else {
// end the viz query
WriteRegister(XE_GPU_REG_VGT_EVENT_INITIATOR, VIZQUERY_END);
XELOGGPU("End viz query ID %.2X", id);
}
return true;
}

View File

@@ -179,13 +179,13 @@ uint32_t GraphicsSystem::ReadRegister(uint32_t addr) {
return 0x08100748;
case 0x0F01: // RB_BC_CONTROL
return 0x0000200E;
case 0x194C: // R500_D1MODE_V_COUNTER(?) / scanline(?)
case 0x194C: // R500_D1MODE_V_COUNTER
return 0x000002D0;
case 0x1951: // ? vblank pending?
return 1;
case 0x1951: // interrupt status
return 1; // vblank
case 0x1961: // AVIVO_D1MODE_VIEWPORT_SIZE
// Screen res - 1280x720
// [width(0x0FFF), height(0x0FFF)]
// maximum [width(0x0FFF), height(0x0FFF)]
return 0x050002D0;
default:
if (!register_file_.GetRegisterInfo(r)) {

View File

@@ -47,6 +47,10 @@ XE_GPU_REGISTER(0x0D04, kDword, SQ_EO_RT)
XE_GPU_REGISTER(0x0C85, kDword, PA_CL_ENHANCE)
// Set with WAIT_UNTIL = WAIT_3D_IDLECLEAN
XE_GPU_REGISTER(0x0E00, kDword, UNKNOWN_0E00)
XE_GPU_REGISTER(0x0E40, kDword, UNKNOWN_0E40)
XE_GPU_REGISTER(0x0E42, kDword, UNKNOWN_0E42)
XE_GPU_REGISTER(0x0F01, kDword, RB_BC_CONTROL)

View File

@@ -16,10 +16,80 @@
#include "xenia/gpu/gpu_flags.h"
#include "xenia/gpu/vulkan/vulkan_gpu_flags.h"
#include "third_party/vulkan/vk_mem_alloc.h"
namespace xe {
namespace gpu {
namespace vulkan {
#if XE_ARCH_AMD64
void copy_cmp_swap_16_unaligned(void* dest_ptr, const void* src_ptr,
uint16_t cmp_value, size_t count) {
auto dest = reinterpret_cast<uint16_t*>(dest_ptr);
auto src = reinterpret_cast<const uint16_t*>(src_ptr);
__m128i shufmask =
_mm_set_epi8(0x0E, 0x0F, 0x0C, 0x0D, 0x0A, 0x0B, 0x08, 0x09, 0x06, 0x07,
0x04, 0x05, 0x02, 0x03, 0x00, 0x01);
__m128i cmpval = _mm_set1_epi16(cmp_value);
size_t i;
for (i = 0; i + 8 <= count; i += 8) {
__m128i input = _mm_loadu_si128(reinterpret_cast<const __m128i*>(&src[i]));
__m128i output = _mm_shuffle_epi8(input, shufmask);
__m128i mask = _mm_cmpeq_epi16(output, cmpval);
output = _mm_or_si128(output, mask);
_mm_storeu_si128(reinterpret_cast<__m128i*>(&dest[i]), output);
}
for (; i < count; ++i) { // handle residual elements
dest[i] = byte_swap(src[i]);
}
}
void copy_cmp_swap_32_unaligned(void* dest_ptr, const void* src_ptr,
uint32_t cmp_value, size_t count) {
auto dest = reinterpret_cast<uint32_t*>(dest_ptr);
auto src = reinterpret_cast<const uint32_t*>(src_ptr);
__m128i shufmask =
_mm_set_epi8(0x0C, 0x0D, 0x0E, 0x0F, 0x08, 0x09, 0x0A, 0x0B, 0x04, 0x05,
0x06, 0x07, 0x00, 0x01, 0x02, 0x03);
__m128i cmpval = _mm_set1_epi32(cmp_value);
size_t i;
for (i = 0; i + 4 <= count; i += 4) {
__m128i input = _mm_loadu_si128(reinterpret_cast<const __m128i*>(&src[i]));
__m128i output = _mm_shuffle_epi8(input, shufmask);
__m128i mask = _mm_cmpeq_epi32(output, cmpval);
output = _mm_or_si128(output, mask);
_mm_storeu_si128(reinterpret_cast<__m128i*>(&dest[i]), output);
}
for (; i < count; ++i) { // handle residual elements
dest[i] = byte_swap(src[i]);
}
}
#else
void copy_and_swap_16_unaligned(void* dest_ptr, const void* src_ptr,
uint16_t cmp_value, size_t count) {
auto dest = reinterpret_cast<uint16_t*>(dest_ptr);
auto src = reinterpret_cast<const uint16_t*>(src_ptr);
for (size_t i = 0; i < count; ++i) {
uint16_t value = byte_swap(src[i]);
dest[i] = value == cmp_value ? 0xFFFF : value;
}
}
void copy_and_swap_32_unaligned(void* dest_ptr, const void* src_ptr,
uint32_t cmp_value, size_t count) {
auto dest = reinterpret_cast<uint32_t*>(dest_ptr);
auto src = reinterpret_cast<const uint32_t*>(src_ptr);
for (size_t i = 0; i < count; ++i) {
uint32_t value = byte_swap(src[i]);
dest[i] = value == cmp_value ? 0xFFFFFFFF : value;
}
}
#endif
using xe::ui::vulkan::CheckResult;
constexpr VkDeviceSize kConstantRegisterUniformRange =
@@ -32,7 +102,7 @@ BufferCache::BufferCache(RegisterFile* register_file, Memory* memory,
device_,
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
capacity);
capacity, 4096);
}
BufferCache::~BufferCache() { Shutdown(); }
@@ -47,6 +117,15 @@ VkResult BufferCache::Initialize() {
return status;
}
// Create a memory allocator for textures.
VmaAllocatorCreateInfo alloc_info = {
0, *device_, *device_, 0, 0, nullptr, nullptr,
};
status = vmaCreateAllocator(&alloc_info, &mem_allocator_);
if (status != VK_SUCCESS) {
return status;
}
// Descriptor pool used for all of our cached descriptors.
// In the steady state we don't allocate anything, so these are all manually
// managed.
@@ -148,28 +227,23 @@ VkResult BufferCache::Initialize() {
}
void BufferCache::Shutdown() {
if (mem_allocator_) {
vmaDestroyAllocator(mem_allocator_);
mem_allocator_ = nullptr;
}
if (transient_descriptor_set_) {
vkFreeDescriptorSets(*device_, descriptor_pool_, 1,
&transient_descriptor_set_);
transient_descriptor_set_ = nullptr;
}
if (descriptor_set_layout_) {
vkDestroyDescriptorSetLayout(*device_, descriptor_set_layout_, nullptr);
descriptor_set_layout_ = nullptr;
}
if (descriptor_pool_) {
vkDestroyDescriptorPool(*device_, descriptor_pool_, nullptr);
descriptor_pool_ = nullptr;
}
VK_SAFE_DESTROY(vkDestroyDescriptorSetLayout, *device_,
descriptor_set_layout_, nullptr);
VK_SAFE_DESTROY(vkDestroyDescriptorPool, *device_, descriptor_pool_, nullptr);
transient_buffer_->Shutdown();
if (gpu_memory_pool_) {
vkFreeMemory(*device_, gpu_memory_pool_, nullptr);
gpu_memory_pool_ = nullptr;
}
VK_SAFE_DESTROY(vkFreeMemory, *device_, gpu_memory_pool_, nullptr);
}
std::pair<VkDeviceSize, VkDeviceSize> BufferCache::UploadConstantRegisters(
@@ -276,13 +350,8 @@ std::pair<VkDeviceSize, VkDeviceSize> BufferCache::UploadConstantRegisters(
std::pair<VkBuffer, VkDeviceSize> BufferCache::UploadIndexBuffer(
VkCommandBuffer command_buffer, uint32_t source_addr,
uint32_t source_length, IndexFormat format, VkFence fence) {
auto offset = FindCachedTransientData(source_addr, source_length);
if (offset != VK_WHOLE_SIZE) {
return {transient_buffer_->gpu_buffer(), offset};
}
// Allocate space in the buffer for our data.
offset = AllocateTransientData(source_length, fence);
auto offset = AllocateTransientData(source_length, fence);
if (offset == VK_WHOLE_SIZE) {
// OOM.
return {nullptr, VK_WHOLE_SIZE};
@@ -290,17 +359,36 @@ std::pair<VkBuffer, VkDeviceSize> BufferCache::UploadIndexBuffer(
const void* source_ptr = memory_->TranslatePhysical(source_addr);
// Copy data into the buffer.
// TODO(benvanik): get min/max indices and pass back?
uint32_t prim_reset_index =
register_file_->values[XE_GPU_REG_VGT_MULTI_PRIM_IB_RESET_INDX].u32;
bool prim_reset_enabled =
!!(register_file_->values[XE_GPU_REG_PA_SU_SC_MODE_CNTL].u32 & (1 << 21));
// Copy data into the buffer. If primitive reset is enabled, translate any
// primitive reset indices to something Vulkan understands.
// TODO(benvanik): memcpy then use compute shaders to swap?
if (format == IndexFormat::kInt16) {
// Endian::k8in16, swap half-words.
xe::copy_and_swap_16_aligned(transient_buffer_->host_base() + offset,
source_ptr, source_length / 2);
} else if (format == IndexFormat::kInt32) {
// Endian::k8in32, swap words.
xe::copy_and_swap_32_aligned(transient_buffer_->host_base() + offset,
source_ptr, source_length / 4);
if (prim_reset_enabled) {
if (format == IndexFormat::kInt16) {
// Endian::k8in16, swap half-words.
copy_cmp_swap_16_unaligned(
transient_buffer_->host_base() + offset, source_ptr,
static_cast<uint16_t>(prim_reset_index), source_length / 2);
} else if (format == IndexFormat::kInt32) {
// Endian::k8in32, swap words.
copy_cmp_swap_32_unaligned(transient_buffer_->host_base() + offset,
source_ptr, prim_reset_index,
source_length / 4);
}
} else {
if (format == IndexFormat::kInt16) {
// Endian::k8in16, swap half-words.
xe::copy_and_swap_16_unaligned(transient_buffer_->host_base() + offset,
source_ptr, source_length / 2);
} else if (format == IndexFormat::kInt32) {
// Endian::k8in32, swap words.
xe::copy_and_swap_32_unaligned(transient_buffer_->host_base() + offset,
source_ptr, source_length / 4);
}
}
transient_buffer_->Flush(offset, source_length);
@@ -321,7 +409,6 @@ std::pair<VkBuffer, VkDeviceSize> BufferCache::UploadIndexBuffer(
VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, 0, nullptr, 1,
&barrier, 0, nullptr);
CacheTransientData(source_addr, source_length, offset);
return {transient_buffer_->gpu_buffer(), offset};
}
@@ -333,29 +420,41 @@ std::pair<VkBuffer, VkDeviceSize> BufferCache::UploadVertexBuffer(
return {transient_buffer_->gpu_buffer(), offset};
}
// Slow path :)
// Expand the region up to the allocation boundary
auto physical_heap = memory_->GetPhysicalHeap();
uint32_t upload_base = source_addr;
uint32_t upload_size = source_length;
// Ping the memory subsystem for allocation size.
// TODO(DrChat): Artifacting occurring in GripShift with this enabled.
// physical_heap->QueryBaseAndSize(&upload_base, &upload_size);
assert(upload_base <= source_addr);
uint32_t source_offset = source_addr - upload_base;
// Allocate space in the buffer for our data.
offset = AllocateTransientData(source_length, fence);
offset = AllocateTransientData(upload_size, fence);
if (offset == VK_WHOLE_SIZE) {
// OOM.
return {nullptr, VK_WHOLE_SIZE};
}
const void* source_ptr = memory_->TranslatePhysical(source_addr);
const void* upload_ptr = memory_->TranslatePhysical(upload_base);
// Copy data into the buffer.
// TODO(benvanik): memcpy then use compute shaders to swap?
if (endian == Endian::k8in32) {
// Endian::k8in32, swap words.
xe::copy_and_swap_32_aligned(transient_buffer_->host_base() + offset,
source_ptr, source_length / 4);
xe::copy_and_swap_32_unaligned(transient_buffer_->host_base() + offset,
upload_ptr, source_length / 4);
} else if (endian == Endian::k16in32) {
xe::copy_and_swap_16_in_32_aligned(transient_buffer_->host_base() + offset,
source_ptr, source_length / 4);
xe::copy_and_swap_16_in_32_unaligned(
transient_buffer_->host_base() + offset, upload_ptr, source_length / 4);
} else {
assert_always();
}
transient_buffer_->Flush(offset, source_length);
transient_buffer_->Flush(offset, upload_size);
// Append a barrier to the command buffer.
VkBufferMemoryBarrier barrier = {
@@ -367,14 +466,14 @@ std::pair<VkBuffer, VkDeviceSize> BufferCache::UploadVertexBuffer(
VK_QUEUE_FAMILY_IGNORED,
transient_buffer_->gpu_buffer(),
offset,
source_length,
upload_size,
};
vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_HOST_BIT,
VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, 0, nullptr, 1,
&barrier, 0, nullptr);
CacheTransientData(source_addr, source_length, offset);
return {transient_buffer_->gpu_buffer(), offset};
CacheTransientData(upload_base, upload_size, offset);
return {transient_buffer_->gpu_buffer(), offset + source_offset};
}
VkDeviceSize BufferCache::AllocateTransientData(VkDeviceSize length,
@@ -407,10 +506,22 @@ VkDeviceSize BufferCache::TryAllocateTransientData(VkDeviceSize length,
VkDeviceSize BufferCache::FindCachedTransientData(uint32_t guest_address,
uint32_t guest_length) {
uint64_t key = uint64_t(guest_length) << 32 | uint64_t(guest_address);
auto it = transient_cache_.find(key);
if (it != transient_cache_.end()) {
return it->second;
if (transient_cache_.empty()) {
// Short-circuit exit.
return VK_WHOLE_SIZE;
}
// Find the first element > guest_address
auto it = transient_cache_.upper_bound(guest_address);
if (it != transient_cache_.begin()) {
// it = first element <= guest_address
--it;
if ((it->first + it->second.first) >= (guest_address + guest_length)) {
// This data is contained within some existing transient data.
auto source_offset = static_cast<VkDeviceSize>(guest_address - it->first);
return it->second.second + source_offset;
}
}
return VK_WHOLE_SIZE;
@@ -419,8 +530,17 @@ VkDeviceSize BufferCache::FindCachedTransientData(uint32_t guest_address,
void BufferCache::CacheTransientData(uint32_t guest_address,
uint32_t guest_length,
VkDeviceSize offset) {
uint64_t key = uint64_t(guest_length) << 32 | uint64_t(guest_address);
transient_cache_[key] = offset;
transient_cache_[guest_address] = {guest_length, offset};
// Erase any entries contained within
auto it = transient_cache_.upper_bound(guest_address);
while (it != transient_cache_.end()) {
if ((guest_address + guest_length) >= (it->first + it->second.first)) {
it = transient_cache_.erase(it);
} else {
break;
}
}
}
void BufferCache::Flush(VkCommandBuffer command_buffer) {

View File

@@ -18,6 +18,8 @@
#include "xenia/ui/vulkan/vulkan.h"
#include "xenia/ui/vulkan/vulkan_device.h"
#include "third_party/vulkan/vk_mem_alloc.h"
#include <map>
namespace xe {
@@ -95,6 +97,15 @@ class BufferCache {
void Scavenge();
private:
// This represents an uploaded vertex buffer.
struct VertexBuffer {
uint32_t guest_address;
uint32_t size;
VmaAllocation alloc;
VmaAllocationInfo alloc_info;
};
// Allocates a block of memory in the transient buffer.
// When memory is not available fences are checked and space is reclaimed.
// Returns VK_WHOLE_SIZE if requested amount of memory is not available.
@@ -115,11 +126,12 @@ class BufferCache {
ui::vulkan::VulkanDevice* device_ = nullptr;
VkDeviceMemory gpu_memory_pool_ = nullptr;
VmaAllocator mem_allocator_ = nullptr;
// Staging ringbuffer we cycle through fast. Used for data we don't
// plan on keeping past the current frame.
std::unique_ptr<ui::vulkan::CircularBuffer> transient_buffer_ = nullptr;
std::map<uint64_t, VkDeviceSize> transient_cache_;
std::map<uint32_t, std::pair<uint32_t, VkDeviceSize>> transient_cache_;
VkDescriptorPool descriptor_pool_ = nullptr;
VkDescriptorSetLayout descriptor_set_layout_ = nullptr;

View File

@@ -534,16 +534,19 @@ bool PipelineCache::SetDynamicState(VkCommandBuffer command_buffer,
if (scissor_state_dirty) {
int32_t ws_x = regs.pa_sc_window_scissor_tl & 0x7FFF;
int32_t ws_y = (regs.pa_sc_window_scissor_tl >> 16) & 0x7FFF;
uint32_t ws_w = (regs.pa_sc_window_scissor_br & 0x7FFF) - ws_x;
uint32_t ws_h = ((regs.pa_sc_window_scissor_br >> 16) & 0x7FFF) - ws_y;
int32_t ws_w = (regs.pa_sc_window_scissor_br & 0x7FFF) - ws_x;
int32_t ws_h = ((regs.pa_sc_window_scissor_br >> 16) & 0x7FFF) - ws_y;
ws_x += window_offset_x;
ws_y += window_offset_y;
int32_t adj_x = ws_x - std::max(ws_x, 0);
int32_t adj_y = ws_y - std::max(ws_y, 0);
VkRect2D scissor_rect;
scissor_rect.offset.x = ws_x;
scissor_rect.offset.y = ws_y;
scissor_rect.extent.width = ws_w;
scissor_rect.extent.height = ws_h;
scissor_rect.offset.x = ws_x - adj_x;
scissor_rect.offset.y = ws_y - adj_y;
scissor_rect.extent.width = std::max(ws_w + adj_x, 0);
scissor_rect.extent.height = std::max(ws_h + adj_y, 0);
vkCmdSetScissor(command_buffer, 0, 1, &scissor_rect);
}
@@ -1209,16 +1212,12 @@ PipelineCache::UpdateStatus PipelineCache::UpdateInputAssemblyState(
// glProvokingVertex(GL_FIRST_VERTEX_CONVENTION);
// }
// Primitive restart index is handled in the buffer cache.
if (regs.pa_su_sc_mode_cntl & (1 << 21)) {
state_info.primitiveRestartEnable = VK_TRUE;
} else {
state_info.primitiveRestartEnable = VK_FALSE;
}
// TODO(benvanik): no way to specify in Vulkan?
assert_true(regs.multi_prim_ib_reset_index == 0xFFFF ||
regs.multi_prim_ib_reset_index == 0xFFFFFF ||
regs.multi_prim_ib_reset_index == 0xFFFFFFFF);
// glPrimitiveRestartIndex(regs.multi_prim_ib_reset_index);
return UpdateStatus::kMismatch;
}

View File

@@ -924,6 +924,7 @@ bool TextureCache::ConvertTexture2D(uint8_t* dest,
}
copy_region->bufferRowLength = src.size_2d.input_width;
copy_region->bufferImageHeight = src.size_2d.input_height;
copy_region->imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
copy_region->imageExtent = {src.size_2d.logical_width,
src.size_2d.logical_height, 1};
return true;
@@ -932,6 +933,7 @@ bool TextureCache::ConvertTexture2D(uint8_t* dest,
TextureSwap(src.endianness, dest, host_address, src.input_length);
copy_region->bufferRowLength = src.size_2d.input_width;
copy_region->bufferImageHeight = src.size_2d.input_height;
copy_region->imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
copy_region->imageExtent = {src.size_2d.logical_width,
src.size_2d.logical_height, 1};
return true;
@@ -996,6 +998,7 @@ bool TextureCache::ConvertTexture2D(uint8_t* dest,
copy_region->bufferRowLength = src.size_2d.input_width;
copy_region->bufferImageHeight = src.size_2d.input_height;
copy_region->imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
copy_region->imageExtent = {src.size_2d.logical_width,
src.size_2d.logical_height, 1};
return true;
@@ -1013,8 +1016,9 @@ bool TextureCache::ConvertTextureCube(uint8_t* dest,
TextureSwap(src.endianness, dest, host_address, src.input_length);
copy_region->bufferRowLength = src.size_cube.input_width;
copy_region->bufferImageHeight = src.size_cube.input_height;
copy_region->imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 6};
copy_region->imageExtent = {src.size_cube.logical_width,
src.size_cube.logical_height, 6};
src.size_cube.logical_height, 1};
return true;
} else {
// TODO(benvanik): optimize this inner loop (or work by tiles).
@@ -1053,8 +1057,9 @@ bool TextureCache::ConvertTextureCube(uint8_t* dest,
copy_region->bufferRowLength = src.size_cube.input_width;
copy_region->bufferImageHeight = src.size_cube.input_height;
copy_region->imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 6};
copy_region->imageExtent = {src.size_cube.logical_width,
src.size_cube.logical_height, 6};
src.size_cube.logical_height, 1};
return true;
}
@@ -1250,7 +1255,9 @@ bool TextureCache::UploadTexture(VkCommandBuffer command_buffer,
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = dest->image;
barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1,
copy_region.imageSubresource.baseArrayLayer,
copy_region.imageSubresource.layerCount};
if (dest->format == VK_FORMAT_D16_UNORM_S8_UINT ||
dest->format == VK_FORMAT_D24_UNORM_S8_UINT ||
dest->format == VK_FORMAT_D32_SFLOAT_S8_UINT) {
@@ -1264,7 +1271,6 @@ bool TextureCache::UploadTexture(VkCommandBuffer command_buffer,
// Now move the converted texture into the destination.
copy_region.bufferOffset = alloc->offset;
copy_region.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
copy_region.imageOffset = {0, 0, 0};
vkCmdCopyBufferToImage(command_buffer, staging_buffer_.gpu_buffer(),
dest->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1,

View File

@@ -866,14 +866,13 @@ bool VulkanCommandProcessor::PopulateVertexBuffers(
// TODO: Make the buffer cache ... actually cache buffers. We can have
// a list of buffers that were cached, and store those in chunks in a
// multiple of the host's page size.
// WRITE WATCHES: We need to invalidate vertex buffers if they're written
// to. Since most vertex buffers aren't aligned to a page boundary, this
// means a watch may cover more than one vertex buffer.
// We need to maintain a list of write watches, and what memory ranges
// they cover. If a vertex buffer lies within a write watch's range, assign
// it to the watch. If there's partial alignment where a buffer lies within
// one watch and outside of it, should we create a new watch or extend the
// existing watch?
// So, we need to track all vertex buffers in a sorted map, and track all
// write watches in a sorted map. When a vertex buffer is uploaded, track
// all untracked pages with 1-page write watches. In the callback,
// invalidate any overlapping vertex buffers.
//
// We would keep the old transient buffer as a staging buffer, and upload
// to a GPU-only buffer that tracks all cached vertex buffers.
auto buffer_ref = buffer_cache_->UploadVertexBuffer(
current_setup_buffer_, physical_address, source_length,
static_cast<Endian>(fetch->endian), current_batch_fence_);

View File

@@ -268,12 +268,15 @@ X_RESULT XmpApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
}
case 0x0007000B: {
assert_true(!buffer_length || buffer_length == 8);
uint32_t xmp_client = xe::load_and_swap<uint32_t>(buffer + 0);
uint32_t float_ptr = xe::load_and_swap<uint32_t>(
buffer + 4); // out ptr to 4b - floating point
assert_true(xmp_client == 0x00000002);
XELOGD("XMPGetVolume(%.8X)", float_ptr);
xe::store_and_swap<float>(memory_->TranslateVirtual(float_ptr), volume_);
struct {
xe::be<uint32_t> xmp_client;
xe::be<uint32_t> volume_ptr;
}* args = memory_->TranslateVirtual<decltype(args)>(buffer_ptr);
assert_true(args->xmp_client == 0x00000002);
XELOGD("XMPGetVolume(%.8X)", uint32_t(args->volume_ptr));
xe::store_and_swap<float>(memory_->TranslateVirtual(args->volume_ptr),
volume_);
return X_ERROR_SUCCESS;
}
case 0x0007000C: {
@@ -349,14 +352,20 @@ X_RESULT XmpApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
return XMPDeleteTitlePlaylist(playlist_handle);
}
case 0x0007001A: {
// XMPSetPlaybackController
assert_true(!buffer_length || buffer_length == 12);
uint32_t xmp_client = xe::load_and_swap<uint32_t>(buffer + 0);
uint32_t unk1 = xe::load_and_swap<uint32_t>(buffer + 4);
uint32_t enabled = xe::load_and_swap<uint32_t>(buffer + 8);
assert_true(xmp_client == 0x00000002);
assert_zero(unk1);
XELOGD("XMPSetEnabled(%.8X, %.8X)", unk1, enabled);
disabled_ = enabled;
struct {
xe::be<uint32_t> xmp_client;
xe::be<uint32_t> controller;
xe::be<uint32_t> locked;
}* args = memory_->TranslateVirtual<decltype(args)>(buffer_ptr);
assert_true(args->xmp_client == 0x00000002);
assert_true(args->controller == 0x00000000);
XELOGD("XMPSetPlaybackController(%.8X, %.8X)", uint32_t(args->controller),
uint32_t(args->locked));
disabled_ = args->locked;
if (disabled_) {
XMPStop(0);
}
@@ -364,22 +373,29 @@ X_RESULT XmpApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
return X_ERROR_SUCCESS;
}
case 0x0007001B: {
// XMPGetPlaybackController
assert_true(!buffer_length || buffer_length == 12);
uint32_t xmp_client = xe::load_and_swap<uint32_t>(buffer + 0);
uint32_t unk_ptr =
xe::load_and_swap<uint32_t>(buffer + 4); // out ptr to 4b - expect 0
uint32_t disabled_ptr = xe::load_and_swap<uint32_t>(
buffer + 8); // out ptr to 4b - expect 1 (to skip)
assert_true(xmp_client == 0x00000002);
XELOGD("XMPGetEnabled(%.8X, %.8X)", unk_ptr, disabled_ptr);
xe::store_and_swap<uint32_t>(memory_->TranslateVirtual(unk_ptr), 0);
xe::store_and_swap<uint32_t>(memory_->TranslateVirtual(disabled_ptr),
disabled_);
struct {
xe::be<uint32_t> xmp_client;
xe::be<uint32_t> controller_ptr;
xe::be<uint32_t> locked_ptr;
}* args = memory_->TranslateVirtual<decltype(args)>(buffer_ptr);
assert_true(args->xmp_client == 0x00000002);
XELOGD("XMPGetPlaybackController(%.8X, %.8X, %.8X)",
uint32_t(args->xmp_client), uint32_t(args->controller_ptr),
uint32_t(args->locked_ptr));
xe::store_and_swap<uint32_t>(
memory_->TranslateVirtual(args->controller_ptr), 0);
xe::store_and_swap<uint32_t>(memory_->TranslateVirtual(args->locked_ptr),
0);
// Atrain spawns a thread 82437FD0 to call this in a tight loop forever.
xe::threading::Sleep(std::chrono::milliseconds(10));
return X_ERROR_SUCCESS;
}
case 0x00070029: {
// XMPGetPlaybackBehavior
assert_true(!buffer_length || buffer_length == 16);
uint32_t xmp_client = xe::load_and_swap<uint32_t>(buffer + 0);
uint32_t playback_mode_ptr = xe::load_and_swap<uint32_t>(buffer + 4);

View File

@@ -273,14 +273,11 @@ dword_result_t NtQueryVirtualMemory(
return X_STATUS_INVALID_PARAMETER;
}
memory_basic_information_ptr->base_address =
static_cast<uint32_t>(alloc_info.base_address);
memory_basic_information_ptr->allocation_base =
static_cast<uint32_t>(alloc_info.allocation_base);
memory_basic_information_ptr->base_address = alloc_info.base_address;
memory_basic_information_ptr->allocation_base = alloc_info.allocation_base;
memory_basic_information_ptr->allocation_protect =
ToXdkProtectFlags(alloc_info.allocation_protect);
memory_basic_information_ptr->region_size =
static_cast<uint32_t>(alloc_info.region_size);
memory_basic_information_ptr->region_size = alloc_info.region_size;
uint32_t x_state = 0;
if (alloc_info.state & kMemoryAllocationReserve) {
x_state |= X_MEM_RESERVE;
@@ -290,7 +287,7 @@ dword_result_t NtQueryVirtualMemory(
}
memory_basic_information_ptr->state = x_state;
memory_basic_information_ptr->protect = ToXdkProtectFlags(alloc_info.protect);
memory_basic_information_ptr->type = alloc_info.type;
memory_basic_information_ptr->type = X_MEM_PRIVATE;
return X_STATUS_SUCCESS;
}

View File

@@ -171,12 +171,14 @@ bool Memory::Initialize() {
heaps_.vE0000000.Initialize(virtual_membase_, 0xE0000000, 0x1FD00000, 4096,
&heaps_.physical);
// Protect the first 64kb of memory.
// Protect the first and last 64kb of memory.
heaps_.v00000000.AllocFixed(
0x00000000, 64 * 1024, 64 * 1024,
0x00000000, 0x10000, 0x10000,
kMemoryAllocationReserve | kMemoryAllocationCommit,
!FLAGS_protect_zero ? kMemoryProtectRead | kMemoryProtectWrite
: kMemoryProtectNoAccess);
heaps_.physical.AllocFixed(0x1FFF0000, 0x10000, 0x10000,
kMemoryAllocationReserve, kMemoryProtectNoAccess);
// GPU writeback.
// 0xC... is physical, 0x7F... is virtual. We may need to overlay these.
@@ -337,6 +339,8 @@ BaseHeap* Memory::LookupHeapByType(bool physical, uint32_t page_size) {
}
}
VirtualHeap* Memory::GetPhysicalHeap() { return &heaps_.physical; }
void Memory::Zero(uint32_t address, uint32_t size) {
std::memset(TranslateVirtual(address), 0, size);
}
@@ -1094,16 +1098,19 @@ bool BaseHeap::QueryRegionInfo(uint32_t base_address,
out_info->region_size = 0;
out_info->state = 0;
out_info->protect = 0;
out_info->type = 0;
if (start_page_entry.state) {
// Committed/reserved region.
out_info->allocation_base = start_page_entry.base_address * page_size_;
out_info->allocation_protect = start_page_entry.allocation_protect;
out_info->allocation_size = start_page_entry.region_page_count * page_size_;
out_info->state = start_page_entry.state;
out_info->protect = start_page_entry.current_protect;
out_info->type = 0x20000;
// Scan forward and report the size of the region matching the initial
// base address's attributes.
for (uint32_t page_number = start_page_number;
page_number < start_page_number + start_page_entry.region_page_count;
page_number <
start_page_entry.base_address + start_page_entry.region_page_count;
++page_number) {
auto page_entry = page_table_[page_number];
if (page_entry.base_address != start_page_entry.base_address ||
@@ -1142,6 +1149,20 @@ bool BaseHeap::QuerySize(uint32_t address, uint32_t* out_size) {
return true;
}
bool BaseHeap::QueryBaseAndSize(uint32_t* in_out_address, uint32_t* out_size) {
uint32_t page_number = (*in_out_address - heap_base_) / page_size_;
if (page_number > page_table_.size()) {
XELOGE("BaseHeap::QuerySize base page out of range");
*out_size = 0;
return false;
}
auto global_lock = global_critical_region_.Acquire();
auto page_entry = page_table_[page_number];
*in_out_address = (page_entry.base_address * page_size_);
*out_size = (page_entry.region_page_count * page_size_);
return true;
}
bool BaseHeap::QueryProtect(uint32_t address, uint32_t* out_protect) {
uint32_t page_number = (address - heap_base_) / page_size_;
if (page_number > page_table_.size()) {

View File

@@ -56,6 +56,8 @@ struct HeapAllocationInfo {
uint32_t allocation_base;
// The memory protection option when the region was initially allocated.
uint32_t allocation_protect;
// The size specified when the region was initially allocated, in bytes.
uint32_t allocation_size;
// The size of the region beginning at the base address in which all pages
// have identical attributes, in bytes.
uint32_t region_size;
@@ -63,8 +65,6 @@ struct HeapAllocationInfo {
uint32_t state;
// The access protection of the pages in the region.
uint32_t protect;
// The type of pages in the region (private).
uint32_t type;
};
// Describes a single page in the page table.
@@ -144,6 +144,9 @@ class BaseHeap {
// Queries the size of the region containing the given address.
bool QuerySize(uint32_t address, uint32_t* out_size);
// Queries the base and size of a region containing the given address.
bool QueryBaseAndSize(uint32_t* in_out_address, uint32_t* out_size);
// Queries the current protection mode of the region containing the given
// address.
bool QueryProtect(uint32_t address, uint32_t* out_protect);
@@ -332,6 +335,9 @@ class Memory {
// Gets the heap with the given properties.
BaseHeap* LookupHeapByType(bool physical, uint32_t page_size);
// Gets the physical base heap.
VirtualHeap* GetPhysicalHeap();
// Dumps a map of all allocated memory to the log.
void DumpMap();

View File

@@ -42,7 +42,7 @@ CircularBuffer::CircularBuffer(VulkanDevice* device, VkBufferUsageFlags usage,
VkMemoryRequirements reqs;
vkGetBufferMemoryRequirements(*device_, gpu_buffer_, &reqs);
alignment_ = reqs.alignment;
alignment_ = xe::round_up(alignment, reqs.alignment);
}
CircularBuffer::~CircularBuffer() { Shutdown(); }

View File

@@ -102,6 +102,7 @@ bool VulkanDevice::Initialize(DeviceInfo device_info) {
ENABLE_AND_EXPECT(shaderCullDistance);
ENABLE_AND_EXPECT(shaderStorageImageExtendedFormats);
ENABLE_AND_EXPECT(shaderTessellationAndGeometryPointSize);
ENABLE_AND_EXPECT(samplerAnisotropy);
ENABLE_AND_EXPECT(geometryShader);
ENABLE_AND_EXPECT(depthClamp);
ENABLE_AND_EXPECT(multiViewport);

View File

@@ -26,10 +26,14 @@ namespace ui {
namespace vulkan {
#define VK_SAFE_DESTROY(fn, dev, obj, alloc) \
if (obj) { \
fn(dev, obj, alloc); \
obj = nullptr; \
}
\
do { \
if (obj) { \
fn(dev, obj, alloc); \
obj = nullptr; \
} \
\
} while (0)
class Fence {
public: