Merge branch 'master' into linux_windowing

This commit is contained in:
Triang3l 2021-08-26 22:58:14 +03:00
commit 7edfdc2672
813 changed files with 429062 additions and 250262 deletions

View File

@ -8,6 +8,7 @@ skip_tags: true
skip_commits:
files:
- .drone.yml
- .github/**
- .travis.yml
- docs/**

214
.drone.yml Normal file
View File

@ -0,0 +1,214 @@
---
kind: pipeline
type: docker
name: lint
# Run this in a separate pipeline so that it will build even if this fails
steps:
- name: lint
image: xeniaproject/buildenv:2021-06-21
commands:
- clang-format --version
- ./xenia-build lint --all
---
kind: pipeline
type: docker
name: x86_64-linux
platform:
os: linux
arch: amd64
# Some expressions in this file are duplicates. Scripting support is
# available using jsonnet but increases complexity
# https://docs.drone.io/pipeline/scripting/jsonnet/
# These volumes will be mounted at the build directory, allowing to
# run different premake toolchains from the same source tree
volumes:
- name: build-premake
temp: {}
- name: build-cmake
temp: {}
steps:
#
# Setup the source tree
#
- name: clone-submodules
image: xeniaproject/buildenv:2021-06-21
commands:
- pwd
# May miss recursive submodules (but faster than xb setup)
- git submodule update --init --depth 1 -j $(nproc)
#
# Setup the two build systems
#
# Native premake Makefiles for production
- name: toolchain-premake
image: xeniaproject/buildenv:2021-06-21
volumes:
- name: build-premake
path: /drone/src/build
commands:
- $CXX --version
- $AR --version
- python3 --version
- ./xenia-build premake
depends_on:
- clone-submodules
# Development toolchain
- name: toolchain-cmake
image: xeniaproject/buildenv:2021-06-21
volumes:
- name: build-cmake
path: /drone/src/build
commands:
- |
./xenia-build premake --devenv=cmake
cd build
for c in Debug Release
do
mkdir cmake-$c
cd cmake-$c
cmake -DCMAKE_BUILD_TYPE=$c ..
cd ..
done
depends_on:
# Premake itself needs to be build first:
- toolchain-premake
#
# Building
#
- name: build-premake-debug-all
image: xeniaproject/buildenv:2021-06-21
volumes:
- name: build-premake
path: /drone/src/build
commands:
- ./xenia-build build --no_premake -j$(nproc) --config=Debug
depends_on:
- toolchain-premake
- name: build-premake-release-tests
image: xeniaproject/buildenv:2021-06-21
volumes:
- name: build-premake
path: /drone/src/build
commands:
- ./xenia-build build --no_premake -j$(nproc) --config=Release --target=xenia-base-tests
depends_on:
- toolchain-premake
- name: build-premake-release-all
image: xeniaproject/buildenv:2021-06-21
volumes:
- name: build-premake
path: /drone/src/build
commands:
- ./xenia-build build --no_premake -j$(nproc) --config=Release
depends_on:
- build-premake-release-tests
- name: build-cmake-debug-all
image: xeniaproject/buildenv:2021-06-21
volumes:
- name: build-cmake
path: /drone/src/build
commands:
- cd build/cmake-Debug
- cmake --build . -j$(nproc)
depends_on:
- toolchain-cmake
- name: build-cmake-release-tests
image: xeniaproject/buildenv:2021-06-21
volumes:
- name: build-cmake
path: /drone/src/build
commands:
- cd build/cmake-Release
- cmake --build . -j$(nproc) --target xenia-base-tests
depends_on:
- toolchain-cmake
- name: build-cmake-release-all
image: xeniaproject/buildenv:2021-06-21
volumes:
- name: build-cmake
path: /drone/src/build
commands:
- cd build/cmake-Release
- cmake --build . -j$(nproc)
depends_on:
- build-cmake-release-tests
#
# Tests
#
- name: test-premake
image: xeniaproject/buildenv:2021-06-21
volumes:
- name: build-premake
path: /drone/src/build
commands:
- ./build/bin/Linux/Release/xenia-base-tests
depends_on:
- build-premake-release-tests
- name: test-cmake
image: xeniaproject/buildenv:2021-06-21
volumes:
- name: build-cmake
path: /drone/src/build
commands:
- ./build/bin/Linux/Release/xenia-base-tests
depends_on:
- build-cmake-release-tests
#
# Stat
#
- name: stat
image: xeniaproject/buildenv:2021-06-21
volumes:
- name: build-premake
path: /build-premake
- name: build-cmake
path: /build-cmake
commands:
- |
header() {
SEP='============================================================'
echo
echo $SEP
echo $@
echo $SEP
}
for v in premake cmake
do
for c in Debug Release
do
header $v $c
p=/build-$v/bin/Linux/$c
ls -la $p
sha256sum $p/*
done
done
depends_on:
- build-premake-debug-all
- build-premake-release-all
- build-cmake-debug-all
- build-cmake-release-all

2
.gitattributes vendored
View File

@ -7,3 +7,5 @@
*.csproj text eol=crlf -whitespace merge=union
*.vcxproj text eol=crlf -whitespace merge=union
*.props text eol=crlf -whitespace merge=union
src/**/shaders/bytecode/** linguist-generated=true

2
.gitmodules vendored
View File

@ -48,7 +48,7 @@
url = https://github.com/jarro2783/cxxopts.git
[submodule "third_party/SDL2"]
path = third_party/SDL2
url = https://github.com/JoelLinn/SDL.git
url = https://github.com/libsdl-org/SDL.git
[submodule "third_party/utfcpp"]
path = third_party/utfcpp
url = https://github.com/xenia-project/utfcpp.git

View File

@ -1,73 +0,0 @@
# Make Travis use docker (for faster builds, in theory).
language: cpp
os:
- linux
dist: bionic
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-9
packages:
- clang-9
- clang-format-9
- llvm-9-dev
- g++-8
- python3
- libc++-dev
- libc++abi-dev
- libgtk-3-dev
- libpthread-stubs0-dev
- libsdl2-dev
#- libvulkan1
#- libvulkan-dev
- libx11-dev
- liblz4-dev
jobs:
include:
- env: C_COMPILER=clang-9 CXX_COMPILER=clang++-9 AR_COMPILER=llvm-ar-9 LINT=true
- env: C_COMPILER=clang-9 CXX_COMPILER=clang++-9 AR_COMPILER=llvm-ar-9 BUILD=true CONFIG=Debug
- env: C_COMPILER=clang-9 CXX_COMPILER=clang++-9 AR_COMPILER=llvm-ar-9 BUILD=true CONFIG=Release
git:
# We handle submodules ourselves in xenia-build setup.
submodules: false
before_script:
- export LIBVULKAN_VERSION=1.1.70
- export CXX=$CXX_COMPILER
- export CC=$C_COMPILER
- export AR=$AR_COMPILER
# Dump useful info.
- $CXX --version
- $AR_COMPILER --version
- python3 --version
- clang-format-9 --version
- clang-format-9 -style=file -dump-config
# Add Vulkan dependencies.
- travis_retry wget http://mirrors.kernel.org/ubuntu/pool/universe/v/vulkan/libvulkan1_$LIBVULKAN_VERSION+dfsg1-1_amd64.deb
- travis_retry wget http://mirrors.kernel.org/ubuntu/pool/universe/v/vulkan/libvulkan-dev_$LIBVULKAN_VERSION+dfsg1-1_amd64.deb
- if [[ $BUILD == true ]]; then sudo dpkg -i libvulkan1_$LIBVULKAN_VERSION+dfsg1-1_amd64.deb libvulkan-dev_$LIBVULKAN_VERSION+dfsg1-1_amd64.deb; fi
# Prepare environment (pull dependencies, build tools).
- travis_retry ./xenia-build setup
script:
# Run linter.
- if [[ $LINT == true ]]; then ./xenia-build lint --all; fi
# Build and run base tests.
- if [[ $BUILD == true ]]; then ./xenia-build build --config=$CONFIG --target=xenia-base-tests; fi
- if [[ $BUILD == true ]]; then ./build/bin/Linux/$CONFIG/xenia-base-tests; fi
# Build and run ppc tests.
- if [[ $BUILD == true ]]; then ./xenia-build build --config=$CONFIG --target=xenia-cpu-ppc-tests; fi
# - if [[ $BUILD == true ]]; then ./build/bin/Linux/$CONFIG/xenia-cpu-ppc-tests --log_file=stdout; fi
# Build all of xenia.
- if [[ $BUILD == true ]]; then ./xenia-build build --config=$CONFIG; fi
# All tests (without haswell support).
#- ./xenia-build test --config=debug --no-build -- --enable_haswell_instructions=false
# All tests (with haswell support).
#- ./xenia-build test --config=debug --no-build -- --enable_haswell_instructions=true

View File

@ -24,7 +24,7 @@ Discussing illegal activities will get you banned.
Buildbot | Status
-------- | ------
[Windows](https://ci.appveyor.com/project/benvanik/xenia/branch/master) | [![Build status](https://ci.appveyor.com/api/projects/status/ftqiy86kdfawyx3a/branch/master?svg=true)](https://ci.appveyor.com/project/benvanik/xenia/branch/master)
[Linux](https://travis-ci.org/xenia-project/xenia) | [![Build status](https://travis-ci.org/xenia-project/xenia.svg?branch=master)](https://travis-ci.org/xenia-project/xenia)
[Linux](https://cloud.drone.io/xenia-project/xenia) | [![Build status](https://cloud.drone.io/api/badges/xenia-project/xenia/status.svg)](https://cloud.drone.io/xenia-project/xenia)
Quite a few real games run. Quite a few don't.
See the [Game compatibility list](https://github.com/xenia-project/game-compatibility/issues)

View File

@ -99,7 +99,7 @@ Clang-9 or newer should be available from system repositories on all up to date
You will also need some development libraries. To get them on an Ubuntu system:
```bash
sudo apt-get install libgtk-3-dev libpthread-stubs0-dev liblz4-dev libx11-dev libvulkan-dev libsdl2-dev libiberty-dev libunwind-dev libc++-dev libc++abi-dev
sudo apt-get install libgtk-3-dev libpthread-stubs0-dev liblz4-dev libx11-dev libx11-xcb-dev libvulkan-dev libsdl2-dev libiberty-dev libunwind-dev libc++-dev libc++abi-dev
```
In addition, you will need up to date Vulkan libraries and drivers for your hardware, which most distributions have in their standard repositories nowadays.

View File

@ -99,8 +99,8 @@ filter("platforms:Linux")
toolset("clang")
buildoptions({
-- "-mlzcnt", -- (don't) Assume lzcnt is supported.
({os.outputof("pkg-config --cflags gtk+-x11-3.0")})[1],
})
pkg_config.all("gtk+-x11-3.0")
links({
"stdc++fs",
"dl",
@ -108,9 +108,6 @@ filter("platforms:Linux")
"pthread",
"rt",
})
linkoptions({
({os.outputof("pkg-config --libs gtk+-3.0")})[1],
})
filter({"platforms:Linux", "kind:*App"})
linkgroups("On")
@ -234,7 +231,6 @@ workspace("xenia")
include("third_party/mspack.lua")
include("third_party/snappy.lua")
include("third_party/spirv-tools.lua")
include("third_party/volk.lua")
include("third_party/xxhash.lua")
if not os.istarget("android") then

View File

@ -24,6 +24,7 @@
#include "xenia/ui/file_picker.h"
#include "xenia/ui/imgui_dialog.h"
#include "xenia/ui/imgui_drawer.h"
#include "xenia/ui/virtual_key.h"
// Autogenerated by `xb premake`.
#include "build/version.h"
@ -53,7 +54,12 @@ EmulatorWindow::EmulatorWindow(Emulator* emulator)
" CHECKED"
#endif
#endif
" (" XE_BUILD_BRANCH "/" XE_BUILD_COMMIT_SHORT "/" XE_BUILD_DATE
" ("
#ifdef XE_BUILD_IS_PR
"PR#" XE_BUILD_PR_NUMBER " " XE_BUILD_PR_REPO
" " XE_BUILD_PR_BRANCH "@" XE_BUILD_PR_COMMIT_SHORT " against "
#endif
XE_BUILD_BRANCH "@" XE_BUILD_COMMIT_SHORT " on " XE_BUILD_DATE
")";
}
@ -93,49 +99,49 @@ bool EmulatorWindow::Initialize() {
window_->on_key_down.AddListener([this](KeyEvent* e) {
bool handled = true;
switch (e->key_code()) {
case 0x4F: { // o
switch (e->virtual_key()) {
case ui::VirtualKey::kO: {
if (e->is_ctrl_pressed()) {
FileOpen();
}
} break;
case 0x6A: { // numpad *
case ui::VirtualKey::kMultiply: {
CpuTimeScalarReset();
} break;
case 0x6D: { // numpad minus
case ui::VirtualKey::kSubtract: {
CpuTimeScalarSetHalf();
} break;
case 0x6B: { // numpad plus
case ui::VirtualKey::kAdd: {
CpuTimeScalarSetDouble();
} break;
case 0x72: { // F3
case ui::VirtualKey::kF3: {
Profiler::ToggleDisplay();
} break;
case 0x73: { // VK_F4
case ui::VirtualKey::kF4: {
GpuTraceFrame();
} break;
case 0x74: { // VK_F5
case ui::VirtualKey::kF5: {
GpuClearCaches();
} break;
case 0x76: { // VK_F7
case ui::VirtualKey::kF7: {
// Save to file
// TODO: Choose path based on user input, or from options
// TODO: Spawn a new thread to do this.
emulator()->SaveToFile("test.sav");
} break;
case 0x77: { // VK_F8
case ui::VirtualKey::kF8: {
// Restore from file
// TODO: Choose path from user
// TODO: Spawn a new thread to do this.
emulator()->RestoreFromFile("test.sav");
} break;
case 0x7A: { // VK_F11
case ui::VirtualKey::kF11: {
ToggleFullscreen();
} break;
case 0x1B: { // VK_ESCAPE
// Allow users to escape fullscreen (but not enter it).
case ui::VirtualKey::kEscape: {
// Allow users to escape fullscreen (but not enter it).
if (window_->is_fullscreen()) {
window_->ToggleFullscreen(false);
} else {
@ -143,18 +149,18 @@ bool EmulatorWindow::Initialize() {
}
} break;
case 0x13: { // VK_PAUSE
case ui::VirtualKey::kPause: {
CpuBreakIntoDebugger();
} break;
case 0x03: { // VK_CANCEL
case ui::VirtualKey::kCancel: {
CpuBreakIntoHostDebugger();
} break;
case 0x70: { // VK_F1
case ui::VirtualKey::kF1: {
ShowHelpWebsite();
} break;
case 0x71: { // VK_F2
case ui::VirtualKey::kF2: {
ShowCommitID();
} break;
@ -425,8 +431,13 @@ void EmulatorWindow::ToggleFullscreen() {
void EmulatorWindow::ShowHelpWebsite() { LaunchWebBrowser("https://xenia.jp"); }
void EmulatorWindow::ShowCommitID() {
#ifdef XE_BUILD_IS_PR
LaunchWebBrowser(
"https://github.com/xenia-project/xenia/pull/" XE_BUILD_PR_NUMBER);
#else
LaunchWebBrowser(
"https://github.com/xenia-project/xenia/commit/" XE_BUILD_COMMIT "/");
#endif
}
void EmulatorWindow::UpdateTitle() {

View File

@ -43,7 +43,6 @@ project("xenia-app")
"mspack",
"snappy",
"spirv-tools",
"volk",
"xxhash",
})
defines({

View File

@ -329,6 +329,22 @@ int xenia_main(const std::vector<std::string>& args) {
emulator->file_system()->RegisterSymbolicLink("cache1:", "\\CACHE1");
}
}
// Some (older?) games try accessing cache:\ too
// NOTE: this must be registered _after_ the cache0/cache1 devices, due to
// substring/start_with logic inside VirtualFileSystem::ResolvePath, else
// accesses to those devices will go here instead
auto cache_device =
std::make_unique<xe::vfs::HostPathDevice>("\\CACHE", "cache", false);
if (!cache_device->Initialize()) {
XELOGE("Unable to scan cache path");
} else {
if (!emulator->file_system()->RegisterDevice(std::move(cache_device))) {
XELOGE("Unable to register cache path");
} else {
emulator->file_system()->RegisterSymbolicLink("cache:", "\\CACHE");
}
}
}
// Set a debug handler.

View File

@ -223,7 +223,7 @@ void AudioSystem::UnregisterClient(size_t index) {
}
bool AudioSystem::Save(ByteStream* stream) {
stream->Write('XAUD');
stream->Write(kAudioSaveSignature);
// Count the number of used clients first.
// Any gaps should be handled gracefully.
@ -251,7 +251,7 @@ bool AudioSystem::Save(ByteStream* stream) {
}
bool AudioSystem::Restore(ByteStream* stream) {
if (stream->Read<uint32_t>() != 'XAUD') {
if (stream->Read<uint32_t>() != kAudioSaveSignature) {
XELOGE("AudioSystem::Restore - Invalid magic value!");
return false;
}

View File

@ -23,6 +23,8 @@
namespace xe {
namespace apu {
constexpr fourcc_t kAudioSaveSignature = make_fourcc("XAUD");
class AudioDriver;
class XmaDecoder;

119
src/xenia/apu/conversion.h Normal file
View File

@ -0,0 +1,119 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_APU_CONVERSION_H_
#define XENIA_APU_CONVERSION_H_
#include <cstdint>
#include "xenia/base/byte_order.h"
#include "xenia/base/platform.h"
namespace xe {
namespace apu {
namespace conversion {
#if XE_ARCH_AMD64
inline void sequential_6_BE_to_interleaved_6_LE(float* output,
const float* input,
size_t ch_sample_count) {
const uint32_t* in = reinterpret_cast<const uint32_t*>(input);
uint32_t* out = reinterpret_cast<uint32_t*>(output);
const __m128i byte_swap_shuffle =
_mm_set_epi8(12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3);
for (size_t sample = 0; sample < ch_sample_count; sample++) {
__m128i sample0 = _mm_set_epi32(
in[3 * ch_sample_count + sample], in[2 * ch_sample_count + sample],
in[1 * ch_sample_count + sample], in[0 * ch_sample_count + sample]);
uint32_t sample1 = in[4 * ch_sample_count + sample];
uint32_t sample2 = in[5 * ch_sample_count + sample];
sample0 = _mm_shuffle_epi8(sample0, byte_swap_shuffle);
_mm_storeu_si128(reinterpret_cast<__m128i*>(&out[sample * 6]), sample0);
sample1 = xe::byte_swap(sample1);
out[sample * 6 + 4] = sample1;
sample2 = xe::byte_swap(sample2);
out[sample * 6 + 5] = sample2;
}
}
inline void sequential_6_BE_to_interleaved_2_LE(float* output,
const float* input,
size_t ch_sample_count) {
assert_true(ch_sample_count % 4 == 0);
const uint32_t* in = reinterpret_cast<const uint32_t*>(input);
uint32_t* out = reinterpret_cast<uint32_t*>(output);
const __m128i byte_swap_shuffle =
_mm_set_epi8(12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3);
const __m128 half = _mm_set1_ps(0.5f);
const __m128 two_fifths = _mm_set1_ps(1.0f / 2.5f);
// put center on left and right, discard low frequency
for (size_t sample = 0; sample < ch_sample_count; sample += 4) {
// load 4 samples from 6 channels each
__m128 fl = _mm_loadu_ps(&input[0 * ch_sample_count + sample]);
__m128 fr = _mm_loadu_ps(&input[1 * ch_sample_count + sample]);
__m128 fc = _mm_loadu_ps(&input[2 * ch_sample_count + sample]);
__m128 bl = _mm_loadu_ps(&input[4 * ch_sample_count + sample]);
__m128 br = _mm_loadu_ps(&input[5 * ch_sample_count + sample]);
// byte swap
fl = _mm_castsi128_ps(
_mm_shuffle_epi8(_mm_castps_si128(fl), byte_swap_shuffle));
fr = _mm_castsi128_ps(
_mm_shuffle_epi8(_mm_castps_si128(fr), byte_swap_shuffle));
fc = _mm_castsi128_ps(
_mm_shuffle_epi8(_mm_castps_si128(fc), byte_swap_shuffle));
bl = _mm_castsi128_ps(
_mm_shuffle_epi8(_mm_castps_si128(bl), byte_swap_shuffle));
br = _mm_castsi128_ps(
_mm_shuffle_epi8(_mm_castps_si128(br), byte_swap_shuffle));
__m128 center_halved = _mm_mul_ps(fc, half);
__m128 left = _mm_add_ps(_mm_add_ps(fl, bl), center_halved);
__m128 right = _mm_add_ps(_mm_add_ps(fr, br), center_halved);
left = _mm_mul_ps(left, two_fifths);
right = _mm_mul_ps(right, two_fifths);
_mm_storeu_ps(&output[sample * 2], _mm_unpacklo_ps(left, right));
_mm_storeu_ps(&output[(sample + 2) * 2], _mm_unpackhi_ps(left, right));
}
}
#else
inline void sequential_6_BE_to_interleaved_6_LE(float* output,
const float* input,
size_t ch_sample_count) {
for (size_t sample = 0; sample < ch_sample_count; sample++) {
for (size_t channel = 0; channel < 6; channel++) {
output[sample * 6 + channel] =
xe::byte_swap(input[channel * ch_sample_count + sample]);
}
}
}
inline void sequential_6_BE_to_interleaved_2_LE(float* output,
const float* input,
size_t ch_sample_count) {
// Default 5.1 channel mapping is fl, fr, fc, lf, bl, br
// https://docs.microsoft.com/en-us/windows/win32/xaudio2/xaudio2-default-channel-mapping
for (size_t sample = 0; sample < ch_sample_count; sample++) {
// put center on left and right, discard low frequency
float fl = xe::byte_swap(input[0 * ch_sample_count + sample]);
float fr = xe::byte_swap(input[1 * ch_sample_count + sample]);
float fc = xe::byte_swap(input[2 * ch_sample_count + sample]);
float br = xe::byte_swap(input[4 * ch_sample_count + sample]);
float bl = xe::byte_swap(input[5 * ch_sample_count + sample]);
float center_halved = fc * 0.5f;
output[sample * 2] = (fl + bl + center_halved) * (1.0f / 2.5f);
output[sample * 2 + 1] = (fr + br + center_halved) * (1.0f / 2.5f);
}
}
#endif
} // namespace conversion
} // namespace apu
} // namespace xe
#endif

View File

@ -10,9 +10,13 @@
#include "xenia/apu/sdl/sdl_audio_driver.h"
#include <array>
#include <cstring>
#include "xenia/apu/apu_flags.h"
#include "xenia/apu/conversion.h"
#include "xenia/base/assert.h"
#include "xenia/base/logging.h"
#include "xenia/base/profiling.h"
#include "xenia/helper/sdl/sdl_helper.h"
namespace xe {
@ -46,41 +50,37 @@ bool SDLAudioDriver::Initialize() {
}
sdl_initialized_ = true;
SDL_AudioCallback audio_callback = [](void* userdata, Uint8* stream,
int len) -> void {
assert_true(len == frame_size_);
const auto driver = static_cast<SDLAudioDriver*>(userdata);
std::unique_lock<std::mutex> guard(driver->frames_mutex_);
if (driver->frames_queued_.empty()) {
memset(stream, 0, len);
} else {
auto buffer = driver->frames_queued_.front();
driver->frames_queued_.pop();
if (cvars::mute) {
memset(stream, 0, len);
} else {
memcpy(stream, buffer, len);
}
driver->frames_unused_.push(buffer);
auto ret = driver->semaphore_->Release(1, nullptr);
assert_true(ret);
SDL_AudioSpec desired_spec = {};
SDL_AudioSpec obtained_spec;
desired_spec.freq = frame_frequency_;
desired_spec.format = AUDIO_F32;
desired_spec.channels = frame_channels_;
desired_spec.samples = channel_samples_;
desired_spec.callback = SDLCallback;
desired_spec.userdata = this;
// Allow the hardware to decide between 5.1 and stereo
int allowed_change = SDL_AUDIO_ALLOW_CHANNELS_CHANGE;
for (int i = 0; i < 2; i++) {
sdl_device_id_ = SDL_OpenAudioDevice(nullptr, 0, &desired_spec,
&obtained_spec, allowed_change);
if (sdl_device_id_ <= 0) {
XELOGE("SDL_OpenAudioDevice() failed.");
return false;
}
};
SDL_AudioSpec wanted_spec = {};
wanted_spec.freq = frame_frequency_;
wanted_spec.format = AUDIO_F32;
wanted_spec.channels = frame_channels_;
wanted_spec.samples = channel_samples_;
wanted_spec.callback = audio_callback;
wanted_spec.userdata = this;
sdl_device_id_ = SDL_OpenAudioDevice(nullptr, 0, &wanted_spec, nullptr, 0);
if (obtained_spec.channels == 2 || obtained_spec.channels == 6) {
break;
}
// If the system is 4 or 7.1, let SDL convert
allowed_change = 0;
SDL_CloseAudioDevice(sdl_device_id_);
sdl_device_id_ = -1;
}
if (sdl_device_id_ <= 0) {
XELOGE("SDL_OpenAudioDevice() failed.");
XELOGE("Failed to get a compatible SDL Audio Device.");
return false;
}
sdl_device_channels_ = obtained_spec.channels;
SDL_PauseAudioDevice(sdl_device_id_, 0);
return true;
@ -99,13 +99,7 @@ void SDLAudioDriver::SubmitFrame(uint32_t frame_ptr) {
}
}
// interleave the data
for (size_t index = 0, o = 0; index < channel_samples_; ++index) {
for (size_t channel = 0, table = 0; channel < frame_channels_;
++channel, table += channel_samples_) {
output_frame[o++] = xe::byte_swap(input_frame[table + index]);
}
}
std::memcpy(output_frame, input_frame, frame_samples_ * sizeof(float));
{
std::unique_lock<std::mutex> guard(frames_mutex_);
@ -133,6 +127,45 @@ void SDLAudioDriver::Shutdown() {
};
}
void SDLAudioDriver::SDLCallback(void* userdata, Uint8* stream, int len) {
SCOPE_profile_cpu_f("apu");
if (!userdata || !stream) {
XELOGE("SDLAudioDriver::sdl_callback called with nullptr.");
return;
}
const auto driver = static_cast<SDLAudioDriver*>(userdata);
assert_true(len ==
sizeof(float) * channel_samples_ * driver->sdl_device_channels_);
std::unique_lock<std::mutex> guard(driver->frames_mutex_);
if (driver->frames_queued_.empty()) {
std::memset(stream, 0, len);
} else {
auto buffer = driver->frames_queued_.front();
driver->frames_queued_.pop();
if (cvars::mute) {
std::memset(stream, 0, len);
} else {
switch (driver->sdl_device_channels_) {
case 2:
conversion::sequential_6_BE_to_interleaved_2_LE(
reinterpret_cast<float*>(stream), buffer, channel_samples_);
break;
case 6:
conversion::sequential_6_BE_to_interleaved_6_LE(
reinterpret_cast<float*>(stream), buffer, channel_samples_);
break;
default:
assert_unhandled_case(driver->sdl_device_channels_);
break;
}
}
driver->frames_unused_.push(buffer);
auto ret = driver->semaphore_->Release(1, nullptr);
assert_true(ret);
}
};
} // namespace sdl
} // namespace apu
} // namespace xe

View File

@ -32,10 +32,13 @@ class SDLAudioDriver : public AudioDriver {
void Shutdown();
protected:
static void SDLCallback(void* userdata, Uint8* stream, int len);
xe::threading::Semaphore* semaphore_ = nullptr;
SDL_AudioDeviceID sdl_device_id_ = -1;
bool sdl_initialized_ = false;
uint8_t sdl_device_channels_ = 0;
static const uint32_t frame_frequency_ = 48000;
static const uint32_t frame_channels_ = 6;

View File

@ -13,6 +13,7 @@
#include "xenia/base/platform_win.h"
#include "xenia/apu/apu_flags.h"
#include "xenia/apu/conversion.h"
#include "xenia/base/clock.h"
#include "xenia/base/logging.h"
@ -208,12 +209,8 @@ void XAudio2AudioDriver::SubmitFrame(uint32_t frame_ptr) {
auto interleave_channels = frame_channels_;
// interleave the data
for (uint32_t index = 0, o = 0; index < channel_samples_; ++index) {
for (uint32_t channel = 0, table = 0; channel < interleave_channels;
++channel, table += channel_samples_) {
output_frame[o++] = xe::byte_swap(input_frame[table + index]);
}
}
conversion::sequential_6_BE_to_interleaved_6_LE(output_frame, input_frame,
channel_samples_);
api::XAUDIO2_BUFFER buffer;
buffer.Flags = 0;

View File

@ -16,6 +16,7 @@
#include "xenia/apu/xma_helpers.h"
#include "xenia/base/bit_stream.h"
#include "xenia/base/logging.h"
#include "xenia/base/platform.h"
#include "xenia/base/profiling.h"
#include "xenia/base/ring_buffer.h"
@ -351,14 +352,12 @@ void XmaContext::Decode(XMA_CONTEXT_DATA* data) {
output_rb.set_read_offset(output_read_offset);
output_rb.set_write_offset(output_write_offset);
int num_channels = data->is_stereo ? 2 : 1;
// We can only decode an entire frame and write it out at a time, so
// don't save any samples.
// TODO(JoelLinn): subframes when looping
size_t output_remaining_bytes = output_rb.write_count();
output_remaining_bytes -=
output_remaining_bytes % (kBytesPerFrameChannel * num_channels);
output_remaining_bytes % (kBytesPerFrameChannel << data->is_stereo);
// is_dirty_ = true; // TODO
// is_dirty_ = false; // TODO
@ -486,7 +485,7 @@ void XmaContext::Decode(XMA_CONTEXT_DATA* data) {
std::tie(frame_count, frame_last_split) = GetPacketFrameCount(packet);
assert_true(frame_count >= 0); // TODO end
PrepareDecoder(packet, data->sample_rate, num_channels);
PrepareDecoder(packet, data->sample_rate, bool(data->is_stereo));
// Current frame is split to next packet:
bool frame_is_split = frame_last_split && (frame_idx >= frame_count - 1);
@ -580,11 +579,11 @@ void XmaContext::Decode(XMA_CONTEXT_DATA* data) {
// assert_true(frame_is_split == (frame_idx == -1));
// dump_raw(av_frame_, id());
ConvertFrame((const uint8_t**)av_frame_->data, num_channels,
kSamplesPerFrame, raw_frame_.data());
ConvertFrame((const uint8_t**)av_frame_->data, bool(data->is_stereo),
raw_frame_.data());
// decoded_consumed_samples_ += kSamplesPerFrame;
auto byte_count = kBytesPerFrameChannel * num_channels;
auto byte_count = kBytesPerFrameChannel << data->is_stereo;
assert_true(output_remaining_bytes >= byte_count);
output_rb.Write(raw_frame_.data(), byte_count);
output_remaining_bytes -= byte_count;
@ -720,7 +719,7 @@ std::tuple<int, int> XmaContext::GetFrameNumber(uint8_t* block, size_t size,
int frame_idx = 0;
while (true) {
if (stream.BitsRemaining() < 15) {
return {packet_idx, -1};
break;
}
if (stream.offset_bits() == bit_offset) {
@ -780,13 +779,15 @@ std::tuple<int, bool> XmaContext::GetPacketFrameCount(uint8_t* packet) {
}
}
int XmaContext::PrepareDecoder(uint8_t* packet, int sample_rate, int channels) {
int XmaContext::PrepareDecoder(uint8_t* packet, int sample_rate,
bool is_two_channel) {
// Sanity check: Packet metadata is always 1 for XMA2/0 for XMA
assert_true((packet[2] & 0x7) == 1 || (packet[2] & 0x7) == 0);
sample_rate = GetSampleRate(sample_rate);
// Re-initialize the context with new sample rate and channels.
uint32_t channels = is_two_channel ? 2 : 1;
if (av_context_->sample_rate != sample_rate ||
av_context_->channels != channels) {
// We have to reopen the codec so it'll realloc whatever data it needs.
@ -805,30 +806,80 @@ int XmaContext::PrepareDecoder(uint8_t* packet, int sample_rate, int channels) {
return 0;
}
bool XmaContext::ConvertFrame(const uint8_t** samples, int num_channels,
int num_samples, uint8_t* output_buffer) {
void XmaContext::ConvertFrame(const uint8_t** samples, bool is_two_channel,
uint8_t* output_buffer) {
// Loop through every sample, convert and drop it into the output array.
// If more than one channel, we need to interleave the samples from each
// channel next to each other.
// TODO: This can definitely be optimized with AVX/SSE intrinsics!
uint32_t o = 0;
for (int i = 0; i < num_samples; i++) {
for (int j = 0; j < num_channels; j++) {
// Select the appropriate array based on the current channel.
auto sample_array = reinterpret_cast<const float*>(samples[j]);
// channel next to each other. Always saturate because FFmpeg output is
// not limited to [-1, 1] (for example 1.095 as seen in RDR)
constexpr float scale = (1 << 15) - 1;
auto out = reinterpret_cast<int16_t*>(output_buffer);
// Raw sample should be within [-1, 1].
// Clamp it, just in case.
float raw_sample = xe::saturate(sample_array[i]);
// Convert the sample and output it in big endian.
float scaled_sample = raw_sample * ((1 << 15) - 1);
int sample = static_cast<int>(scaled_sample);
xe::store_and_swap<uint16_t>(&output_buffer[o++ * 2], sample & 0xFFFF);
// For testing of vectorized versions, stereo audio is common in Halo 3, since
// the first menu frame; the intro cutscene also has more than 2 channels.
#if XE_ARCH_AMD64
static_assert(kSamplesPerFrame % 8 == 0);
const auto in_channel_0 = reinterpret_cast<const float*>(samples[0]);
const __m128 scale_mm = _mm_set1_ps(scale);
if (is_two_channel) {
const auto in_channel_1 = reinterpret_cast<const float*>(samples[1]);
const __m128i shufmask =
_mm_set_epi8(14, 15, 6, 7, 12, 13, 4, 5, 10, 11, 2, 3, 8, 9, 0, 1);
for (uint32_t i = 0; i < kSamplesPerFrame; i += 4) {
// Load 8 samples, 4 for each channel.
__m128 in_mm0 = _mm_loadu_ps(&in_channel_0[i]);
__m128 in_mm1 = _mm_loadu_ps(&in_channel_1[i]);
// Rescale.
in_mm0 = _mm_mul_ps(in_mm0, scale_mm);
in_mm1 = _mm_mul_ps(in_mm1, scale_mm);
// Cast to int32.
__m128i out_mm0 = _mm_cvtps_epi32(in_mm0);
__m128i out_mm1 = _mm_cvtps_epi32(in_mm1);
// Saturated cast and pack to int16.
__m128i out_mm = _mm_packs_epi32(out_mm0, out_mm1);
// Interleave channels and byte swap.
out_mm = _mm_shuffle_epi8(out_mm, shufmask);
// Store, as [out + i * 4] movdqu.
_mm_storeu_si128(reinterpret_cast<__m128i*>(&out[i * 2]), out_mm);
}
} else {
const __m128i shufmask =
_mm_set_epi8(14, 15, 12, 13, 10, 11, 8, 9, 6, 7, 4, 5, 2, 3, 0, 1);
for (uint32_t i = 0; i < kSamplesPerFrame; i += 8) {
// Load 8 samples, as [in_channel_0 + i * 4] and
// [in_channel_0 + i * 4 + 16] movups.
__m128 in_mm0 = _mm_loadu_ps(&in_channel_0[i]);
__m128 in_mm1 = _mm_loadu_ps(&in_channel_0[i + 4]);
// Rescale.
in_mm0 = _mm_mul_ps(in_mm0, scale_mm);
in_mm1 = _mm_mul_ps(in_mm1, scale_mm);
// Cast to int32.
__m128i out_mm0 = _mm_cvtps_epi32(in_mm0);
__m128i out_mm1 = _mm_cvtps_epi32(in_mm1);
// Saturated cast and pack to int16.
__m128i out_mm = _mm_packs_epi32(out_mm0, out_mm1);
// Byte swap.
out_mm = _mm_shuffle_epi8(out_mm, shufmask);
// Store, as [out + i * 2] movdqu.
_mm_storeu_si128(reinterpret_cast<__m128i*>(&out[i]), out_mm);
}
}
#else
uint32_t o = 0;
for (uint32_t i = 0; i < kSamplesPerFrame; i++) {
for (uint32_t j = 0; j <= uint32_t(is_two_channel); j++) {
// Select the appropriate array based on the current channel.
auto in = reinterpret_cast<const float*>(samples[j]);
return true;
// Raw samples sometimes aren't within [-1, 1]
float scaled_sample = xe::saturate_signed(in[i]) * scale;
// Convert the sample and output it in big endian.
auto sample = static_cast<int16_t>(scaled_sample);
out[o++] = xe::byte_swap(sample);
}
}
#endif
}
} // namespace apu

View File

@ -186,13 +186,13 @@ class XmaContext {
static std::tuple<int, bool> GetPacketFrameCount(uint8_t* packet);
// Convert sample format and swap bytes
static bool ConvertFrame(const uint8_t** samples, int num_channels,
int num_samples, uint8_t* output_buffer);
static void ConvertFrame(const uint8_t** samples, bool is_two_channel,
uint8_t* output_buffer);
bool ValidFrameOffset(uint8_t* block, size_t size_bytes,
size_t frame_offset_bits);
void Decode(XMA_CONTEXT_DATA* data);
int PrepareDecoder(uint8_t* packet, int sample_rate, int channels);
int PrepareDecoder(uint8_t* packet, int sample_rate, bool is_two_channel);
Memory* memory_ = nullptr;

View File

@ -123,7 +123,7 @@ X_STATUS XmaDecoder::Setup(kernel::KernelState* kernel_state) {
sizeof(XMA_CONTEXT_DATA) * kContextCount, 256, kSystemHeapPhysical);
context_data_last_ptr_ =
context_data_first_ptr_ + (sizeof(XMA_CONTEXT_DATA) * kContextCount - 1);
register_file_[XE_XMA_REG_CONTEXT_ARRAY_ADDRESS].u32 =
register_file_[XmaRegister::ContextArrayAddress] =
memory()->GetPhysicalAddress(context_data_first_ptr_);
// Setup XMA contexts.
@ -134,7 +134,7 @@ X_STATUS XmaDecoder::Setup(kernel::KernelState* kernel_state) {
assert_always();
}
}
register_file_[XE_XMA_REG_NEXT_CONTEXT_INDEX].u32 = 1;
register_file_[XmaRegister::NextContextIndex] = 1;
context_bitmap_.Resize(kContextCount);
worker_running_ = true;
@ -254,27 +254,29 @@ bool XmaDecoder::BlockOnContext(uint32_t guest_ptr, bool poll) {
}
uint32_t XmaDecoder::ReadRegister(uint32_t addr) {
uint32_t r = (addr & 0xFFFF) / 4;
auto r = (addr & 0xFFFF) / 4;
assert_true(r < XmaRegisterFile::kRegisterCount);
switch (r) {
case XE_XMA_REG_CURRENT_CONTEXT_INDEX: {
case XmaRegister::ContextArrayAddress:
break;
case XmaRegister::CurrentContextIndex: {
// 0606h (1818h) is rotating context processing # set to hardware ID of
// context being processed.
// If bit 200h is set, the locking code will possibly collide on hardware
// IDs and error out, so we should never set it (I think?).
uint32_t& current_context_index =
register_file_[XE_XMA_REG_CURRENT_CONTEXT_INDEX].u32;
register_file_[XmaRegister::CurrentContextIndex];
uint32_t& next_context_index =
register_file_[XE_XMA_REG_NEXT_CONTEXT_INDEX].u32;
register_file_[XmaRegister::NextContextIndex];
// To prevent games from seeing a stuck XMA context, return a rotating
// number.
current_context_index = next_context_index;
next_context_index = (next_context_index + 1) % kContextCount;
break;
}
default: {
default:
const auto register_info = register_file_.GetRegisterInfo(r);
if (register_info) {
XELOGW("XMA: Read from unhandled register ({:04X}, {})", r,
@ -283,10 +285,9 @@ uint32_t XmaDecoder::ReadRegister(uint32_t addr) {
XELOGW("XMA: Read from unknown register ({:04X})", r);
}
break;
}
}
return xe::byte_swap(register_file_.values[r].u32);
return xe::byte_swap(register_file_[r]);
}
void XmaDecoder::WriteRegister(uint32_t addr, uint32_t value) {
@ -296,16 +297,16 @@ void XmaDecoder::WriteRegister(uint32_t addr, uint32_t value) {
value = xe::byte_swap(value);
assert_true(r < XmaRegisterFile::kRegisterCount);
register_file_.values[r].u32 = value;
register_file_[r] = value;
if (r >= XE_XMA_REG_CONTEXT_KICK_0 && r <= XE_XMA_REG_CONTEXT_KICK_9) {
if (r >= XmaRegister::Context0Kick && r <= XmaRegister::Context9Kick) {
// Context kick command.
// This will kick off the given hardware contexts.
// Basically, this kicks the SPU and says "hey, decode that audio!"
// XMAEnableContext
// The context ID is a bit in the range of the entire context array.
uint32_t base_context_id = (r - XE_XMA_REG_CONTEXT_KICK_0) * 32;
uint32_t base_context_id = (r - XmaRegister::Context0Kick) * 32;
for (int i = 0; value && i < 32; ++i, value >>= 1) {
if (value & 1) {
uint32_t context_id = base_context_id + i;
@ -315,11 +316,11 @@ void XmaDecoder::WriteRegister(uint32_t addr, uint32_t value) {
}
// Signal the decoder thread to start processing.
work_event_->Set();
} else if (r >= XE_XMA_REG_CONTEXT_LOCK_0 && r <= XE_XMA_REG_CONTEXT_LOCK_9) {
} else if (r >= XmaRegister::Context0Lock && r <= XmaRegister::Context9Lock) {
// Context lock command.
// This requests a lock by flagging the context.
// XMADisableContext
uint32_t base_context_id = (r - XE_XMA_REG_CONTEXT_LOCK_0) * 32;
uint32_t base_context_id = (r - XmaRegister::Context0Lock) * 32;
for (int i = 0; value && i < 32; ++i, value >>= 1) {
if (value & 1) {
uint32_t context_id = base_context_id + i;
@ -328,12 +329,12 @@ void XmaDecoder::WriteRegister(uint32_t addr, uint32_t value) {
}
}
// Signal the decoder thread to start processing.
work_event_->Set();
} else if (r >= XE_XMA_REG_CONTEXT_CLEAR_0 &&
r <= XE_XMA_REG_CONTEXT_CLEAR_9) {
// work_event_->Set();
} else if (r >= XmaRegister::Context0Clear &&
r <= XmaRegister::Context9Clear) {
// Context clear command.
// This will reset the given hardware contexts.
uint32_t base_context_id = (r - XE_XMA_REG_CONTEXT_CLEAR_0) * 32;
uint32_t base_context_id = (r - XmaRegister::Context0Clear) * 32;
for (int i = 0; value && i < 32; ++i, value >>= 1) {
if (value & 1) {
uint32_t context_id = base_context_id + i;

View File

@ -2,7 +2,7 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
@ -37,7 +37,7 @@ class XmaDecoder {
void Shutdown();
uint32_t context_array_ptr() const {
return register_file_.values[XE_XMA_REG_CONTEXT_ARRAY_ADDRESS].u32;
return register_file_[XmaRegister::ContextArrayAddress];
}
uint32_t AllocateContext();

View File

@ -2,7 +2,7 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
@ -20,10 +20,9 @@ XmaRegisterFile::XmaRegisterFile() { std::memset(values, 0, sizeof(values)); }
const XmaRegisterInfo* XmaRegisterFile::GetRegisterInfo(uint32_t index) {
switch (index) {
#define XE_XMA_REGISTER(index, type, name) \
#define XE_XMA_REGISTER(index, name) \
case index: { \
static const XmaRegisterInfo reg_info = { \
XmaRegisterInfo::Type::type, \
#name, \
}; \
return &reg_info; \

View File

@ -2,7 +2,7 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
@ -16,18 +16,13 @@
namespace xe {
namespace apu {
enum XmaRegister {
#define XE_XMA_REGISTER(index, type, name) XE_XMA_REG_##name = index,
struct XmaRegister {
#define XE_XMA_REGISTER(index, name) static const uint32_t name = index;
#include "xenia/apu/xma_register_table.inc"
#undef XE_XMA_REGISTER
};
struct XmaRegisterInfo {
enum class Type {
kDword,
kFloat,
};
Type type;
const char* name;
};
@ -38,14 +33,10 @@ class XmaRegisterFile {
static const XmaRegisterInfo* GetRegisterInfo(uint32_t index);
static const size_t kRegisterCount = (0xFFFF + 1) / 4;
union RegisterValue {
uint32_t u32;
float f32;
};
RegisterValue values[kRegisterCount];
uint32_t values[kRegisterCount];
RegisterValue& operator[](int reg) { return values[reg]; }
RegisterValue& operator[](XmaRegister reg) { return values[reg]; }
uint32_t operator[](uint32_t reg) const { return values[reg]; }
uint32_t& operator[](uint32_t reg) { return values[reg]; }
};
} // namespace apu

View File

@ -2,7 +2,7 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
@ -10,42 +10,72 @@
// This is a partial file designed to be included by other files when
// constructing various tables.
//#define XE_XMA_REGISTER(index, type, name)
#ifndef XE_XMA_REGISTER
#define XE_XMA_REGISTER(index, name)
#define __XE_XMA_REGISTER_UNSET
#endif
XE_XMA_REGISTER(0x0600, kDword, CONTEXT_ARRAY_ADDRESS)
#ifndef XE_XMA_REGISTER_CONTEXT_GROUP
#define XE_XMA_REGISTER_CONTEXT_GROUP(index, suffix) \
XE_XMA_REGISTER(index + 0, Context0##suffix) \
XE_XMA_REGISTER(index + 1, Context1##suffix) \
XE_XMA_REGISTER(index + 2, Context2##suffix) \
XE_XMA_REGISTER(index + 3, Context3##suffix) \
XE_XMA_REGISTER(index + 4, Context4##suffix) \
XE_XMA_REGISTER(index + 5, Context5##suffix) \
XE_XMA_REGISTER(index + 6, Context6##suffix) \
XE_XMA_REGISTER(index + 7, Context7##suffix) \
XE_XMA_REGISTER(index + 8, Context8##suffix) \
XE_XMA_REGISTER(index + 9, Context9##suffix)
#endif
XE_XMA_REGISTER(0x0606, kDword, CURRENT_CONTEXT_INDEX)
XE_XMA_REGISTER(0x0607, kDword, NEXT_CONTEXT_INDEX)
// 0x0000..0x001F : ???
// 0x0020..0x03FF : all 0xFFs?
// 0x0400..0x043F : ???
// 0x0440..0x047F : all 0xFFs?
// 0x0480..0x048B : ???
// 0x048C..0x04C0 : all 0xFFs?
// 0x04C1..0x04CB : ???
// 0x04CC..0x04FF : all 0xFFs?
// 0x0500..0x051F : ???
// 0x0520..0x057F : all 0xFFs?
// 0x0580..0x058F : ???
// 0x0590..0x05FF : all 0xFFs?
XE_XMA_REGISTER(0x0650, kDword, CONTEXT_KICK_0)
XE_XMA_REGISTER(0x0651, kDword, CONTEXT_KICK_1)
XE_XMA_REGISTER(0x0652, kDword, CONTEXT_KICK_2)
XE_XMA_REGISTER(0x0653, kDword, CONTEXT_KICK_3)
XE_XMA_REGISTER(0x0654, kDword, CONTEXT_KICK_4)
XE_XMA_REGISTER(0x0655, kDword, CONTEXT_KICK_5)
XE_XMA_REGISTER(0x0656, kDword, CONTEXT_KICK_6)
XE_XMA_REGISTER(0x0657, kDword, CONTEXT_KICK_7)
XE_XMA_REGISTER(0x0658, kDword, CONTEXT_KICK_8)
XE_XMA_REGISTER(0x0659, kDword, CONTEXT_KICK_9)
// XMA stuff is probably only 0x0600..0x06FF
//---------------------------------------------------------------------------//
XE_XMA_REGISTER(0x0690, kDword, CONTEXT_LOCK_0)
XE_XMA_REGISTER(0x0691, kDword, CONTEXT_LOCK_1)
XE_XMA_REGISTER(0x0692, kDword, CONTEXT_LOCK_2)
XE_XMA_REGISTER(0x0693, kDword, CONTEXT_LOCK_3)
XE_XMA_REGISTER(0x0694, kDword, CONTEXT_LOCK_4)
XE_XMA_REGISTER(0x0695, kDword, CONTEXT_LOCK_5)
XE_XMA_REGISTER(0x0696, kDword, CONTEXT_LOCK_6)
XE_XMA_REGISTER(0x0697, kDword, CONTEXT_LOCK_7)
XE_XMA_REGISTER(0x0698, kDword, CONTEXT_LOCK_8)
XE_XMA_REGISTER(0x0699, kDword, CONTEXT_LOCK_9)
XE_XMA_REGISTER(0x0600, ContextArrayAddress)
// 0x0601..0x0605 : ???
XE_XMA_REGISTER(0x0606, CurrentContextIndex)
XE_XMA_REGISTER(0x0607, NextContextIndex)
// 0x0608 : ???
// 0x0609..0x060F : zero?
XE_XMA_REGISTER_CONTEXT_GROUP(0x0610, Unknown610)
// 0x061A..0x061F : zero?
XE_XMA_REGISTER_CONTEXT_GROUP(0x0620, Unknown620)
// 0x062A..0x0641 : zero?
// 0x0642..0x0644 : ???
// 0x0645..0x064F : zero?
XE_XMA_REGISTER_CONTEXT_GROUP(0x0650, Kick)
// 0x065A..0x065F : zero?
XE_XMA_REGISTER_CONTEXT_GROUP(0x0660, Unknown660)
// 0x066A..0x0681 : zero?
// 0x0682..0x0684 : ???
// 0x0685..0x068F : zero?
XE_XMA_REGISTER_CONTEXT_GROUP(0x0690, Lock)
// 0x069A..0x069F : zero?
XE_XMA_REGISTER_CONTEXT_GROUP(0x06A0, Clear)
XE_XMA_REGISTER(0x06A0, kDword, CONTEXT_CLEAR_0)
XE_XMA_REGISTER(0x06A1, kDword, CONTEXT_CLEAR_1)
XE_XMA_REGISTER(0x06A2, kDword, CONTEXT_CLEAR_2)
XE_XMA_REGISTER(0x06A3, kDword, CONTEXT_CLEAR_3)
XE_XMA_REGISTER(0x06A4, kDword, CONTEXT_CLEAR_4)
XE_XMA_REGISTER(0x06A5, kDword, CONTEXT_CLEAR_5)
XE_XMA_REGISTER(0x06A6, kDword, CONTEXT_CLEAR_6)
XE_XMA_REGISTER(0x06A7, kDword, CONTEXT_CLEAR_7)
XE_XMA_REGISTER(0x06A8, kDword, CONTEXT_CLEAR_8)
XE_XMA_REGISTER(0x06A9, kDword, CONTEXT_CLEAR_9)
//---------------------------------------------------------------------------//
// 0x0700..0x07FF : all 0xFFs?
// 0x0800..0x17FF : ???
// 0x1800..0x2FFF : all 0xFFs?
// 0x3000..0x30FF : ???
// 0x3100..0x3FFF : all 0xFFs?
#ifdef __XE_XMA_REGISTER_UNSET
#undef __XE_XMA_REGISTER_UNSET
#undef XE_XMA_REGISTER
#endif

View File

@ -2,7 +2,7 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
@ -13,6 +13,7 @@
#include <memory>
#include "xenia/base/assert.h"
#include "xenia/base/math.h"
namespace xe {
@ -45,12 +46,25 @@ void Arena::DebugFill() {
}
}
void* Arena::Alloc(size_t size) {
void* Arena::Alloc(size_t size, size_t align) {
assert_true(
xe::bit_count(align) == 1 && align <= 16,
"align needs to be a power of 2 and not greater than Chunk alignment");
// for alignment
const auto get_padding = [this, align]() -> size_t {
const size_t mask = align - 1;
size_t deviation = active_chunk_->offset & mask;
return (align - deviation) & mask;
};
if (active_chunk_) {
if (active_chunk_->capacity - active_chunk_->offset < size + 4096) {
if (active_chunk_->capacity - active_chunk_->offset <
size + get_padding() + 4096) {
Chunk* next = active_chunk_->next;
if (!next) {
assert_true(size < chunk_size_, "need to support larger chunks");
assert_true(size + get_padding() < chunk_size_,
"need to support larger chunks");
next = new Chunk(chunk_size_);
active_chunk_->next = next;
}
@ -61,8 +75,11 @@ void* Arena::Alloc(size_t size) {
head_chunk_ = active_chunk_ = new Chunk(chunk_size_);
}
active_chunk_->offset += get_padding();
uint8_t* p = active_chunk_->buffer + active_chunk_->offset;
active_chunk_->offset += size;
assert_true((reinterpret_cast<size_t>(p) & (align - 1)) == 0,
"alignment failed");
return p;
}
@ -113,6 +130,8 @@ void Arena::CloneContents(void* buffer, size_t buffer_length) {
Arena::Chunk::Chunk(size_t chunk_size)
: next(nullptr), capacity(chunk_size), buffer(0), offset(0) {
buffer = reinterpret_cast<uint8_t*>(malloc(capacity));
assert_true((reinterpret_cast<size_t>(buffer) & size_t(15)) == 0,
"16 byte alignment required");
}
Arena::Chunk::~Chunk() {

View File

@ -2,7 +2,7 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
@ -24,11 +24,13 @@ class Arena {
void Reset();
void DebugFill();
void* Alloc(size_t size);
void* Alloc(size_t size, size_t align);
template <typename T>
T* Alloc() {
return reinterpret_cast<T*>(Alloc(sizeof(T)));
return reinterpret_cast<T*>(Alloc(sizeof(T), alignof(T)));
}
// When rewinding aligned allocations, any padding that was applied during
// allocation will be leaked
void Rewind(size_t size);
void* CloneContents();

View File

@ -11,103 +11,116 @@
#define XENIA_BASE_BYTE_ORDER_H_
#include <cstdint>
#if defined __has_include
#if __has_include(<version>)
#include <version>
#endif
#endif
#if __cpp_lib_endian
#include <bit>
#endif
#include "xenia/base/assert.h"
#include "xenia/base/platform.h"
#if XE_PLATFORM_LINUX
#include <byteswap.h>
#if !__cpp_lib_endian
// Polyfill
#ifdef __BYTE_ORDER__
namespace std {
enum class endian {
little = __ORDER_LITTLE_ENDIAN__,
big = __ORDER_BIG_ENDIAN__,
native = __BYTE_ORDER__
};
}
#else
// Hardcode to little endian for now
namespace std {
enum class endian { little = 0, big = 1, native = 0 };
}
#endif
#endif
// Check for mixed endian
static_assert((std::endian::native == std::endian::big) ||
(std::endian::native == std::endian::little));
namespace xe {
#if XE_PLATFORM_WIN32
#if XE_COMPILER_MSVC
#define XENIA_BASE_BYTE_SWAP_16 _byteswap_ushort
#define XENIA_BASE_BYTE_SWAP_32 _byteswap_ulong
#define XENIA_BASE_BYTE_SWAP_64 _byteswap_uint64
#elif XE_PLATFORM_MAC
#define XENIA_BASE_BYTE_SWAP_16 OSSwapInt16
#define XENIA_BASE_BYTE_SWAP_32 OSSwapInt32
#define XENIA_BASE_BYTE_SWAP_64 OSSwapInt64
#else
#define XENIA_BASE_BYTE_SWAP_16 bswap_16
#define XENIA_BASE_BYTE_SWAP_32 bswap_32
#define XENIA_BASE_BYTE_SWAP_64 bswap_64
#define XENIA_BASE_BYTE_SWAP_16 __builtin_bswap16
#define XENIA_BASE_BYTE_SWAP_32 __builtin_bswap32
#define XENIA_BASE_BYTE_SWAP_64 __builtin_bswap64
#endif // XE_PLATFORM_WIN32
inline int8_t byte_swap(int8_t value) { return value; }
inline uint8_t byte_swap(uint8_t value) { return value; }
inline int16_t byte_swap(int16_t value) {
return static_cast<int16_t>(
XENIA_BASE_BYTE_SWAP_16(static_cast<int16_t>(value)));
}
inline uint16_t byte_swap(uint16_t value) {
return XENIA_BASE_BYTE_SWAP_16(value);
}
inline uint16_t byte_swap(char16_t value) {
return static_cast<char16_t>(XENIA_BASE_BYTE_SWAP_16(value));
}
inline int32_t byte_swap(int32_t value) {
return static_cast<int32_t>(
XENIA_BASE_BYTE_SWAP_32(static_cast<int32_t>(value)));
}
inline uint32_t byte_swap(uint32_t value) {
return XENIA_BASE_BYTE_SWAP_32(value);
}
inline int64_t byte_swap(int64_t value) {
return static_cast<int64_t>(
XENIA_BASE_BYTE_SWAP_64(static_cast<int64_t>(value)));
}
inline uint64_t byte_swap(uint64_t value) {
return XENIA_BASE_BYTE_SWAP_64(value);
}
inline float byte_swap(float value) {
uint32_t temp = byte_swap(*reinterpret_cast<uint32_t*>(&value));
return *reinterpret_cast<float*>(&temp);
}
inline double byte_swap(double value) {
uint64_t temp = byte_swap(*reinterpret_cast<uint64_t*>(&value));
return *reinterpret_cast<double*>(&temp);
}
template <typename T>
template <class T>
inline T byte_swap(T value) {
if (sizeof(T) == 4) {
return static_cast<T>(byte_swap(static_cast<uint32_t>(value)));
} else if (sizeof(T) == 2) {
return static_cast<T>(byte_swap(static_cast<uint16_t>(value)));
} else {
assert_always("not handled");
static_assert(
sizeof(T) == 8 || sizeof(T) == 4 || sizeof(T) == 2 || sizeof(T) == 1,
"byte_swap(T value): Type T has illegal size");
if constexpr (sizeof(T) == 8) {
uint64_t temp =
XENIA_BASE_BYTE_SWAP_64(*reinterpret_cast<uint64_t*>(&value));
return *reinterpret_cast<T*>(&temp);
} else if constexpr (sizeof(T) == 4) {
uint32_t temp =
XENIA_BASE_BYTE_SWAP_32(*reinterpret_cast<uint32_t*>(&value));
return *reinterpret_cast<T*>(&temp);
} else if constexpr (sizeof(T) == 2) {
uint16_t temp =
XENIA_BASE_BYTE_SWAP_16(*reinterpret_cast<uint16_t*>(&value));
return *reinterpret_cast<T*>(&temp);
} else if constexpr (sizeof(T) == 1) {
return value;
}
}
template <typename T>
struct be {
be() = default;
be(const T& src) : value(xe::byte_swap(src)) {} // NOLINT(runtime/explicit)
be(const be& other) { value = other.value; } // NOLINT(runtime/explicit)
operator T() const { return xe::byte_swap(value); }
template <typename T, std::endian E>
struct endian_store {
endian_store() = default;
endian_store(const T& src) { set(src); }
endian_store(const endian_store& other) { set(other); }
operator T() const { return get(); }
be<T>& operator+=(int a) {
void set(const T& src) {
if constexpr (std::endian::native == E) {
value = src;
} else {
value = xe::byte_swap(src);
}
}
void set(const endian_store& other) { value = other.value; }
T get() const {
if constexpr (std::endian::native == E) {
return value;
}
return xe::byte_swap(value);
}
endian_store<T, E>& operator+=(int a) {
*this = *this + a;
return *this;
}
be<T>& operator-=(int a) {
endian_store<T, E>& operator-=(int a) {
*this = *this - a;
return *this;
}
be<T>& operator++() {
endian_store<T, E>& operator++() {
*this += 1;
return *this;
} // ++a
be<T> operator++(int) {
endian_store<T, E> operator++(int) {
*this += 1;
return (*this - 1);
} // a++
be<T>& operator--() {
endian_store<T, E>& operator--() {
*this -= 1;
return *this;
} // --a
be<T> operator--(int) {
endian_store<T, E> operator--(int) {
*this -= 1;
return (*this + 1);
} // a--
@ -115,6 +128,11 @@ struct be {
T value;
};
template <typename T>
using be = endian_store<T, std::endian::big>;
template <typename T>
using le = endian_store<T, std::endian::little>;
} // namespace xe
#endif // XENIA_BASE_BYTE_ORDER_H_

View File

@ -20,18 +20,19 @@ ByteStream::ByteStream(uint8_t* data, size_t data_length, size_t offset)
ByteStream::~ByteStream() = default;
void ByteStream::Advance(size_t num_bytes) { offset_ += num_bytes; }
void ByteStream::Advance(size_t num_bytes) {
assert_true(offset_ + num_bytes <= data_length_);
offset_ += num_bytes;
}
void ByteStream::Read(uint8_t* buf, size_t len) {
assert_true(offset_ < data_length_);
assert_true(offset_ + len <= data_length_);
std::memcpy(buf, data_ + offset_, len);
Advance(len);
}
void ByteStream::Write(const uint8_t* buf, size_t len) {
assert_true(offset_ < data_length_);
assert_true(offset_ + len <= data_length_);
std::memcpy(data_ + offset_, buf, len);
Advance(len);
}
@ -41,7 +42,6 @@ std::string ByteStream::Read() {
std::string str;
uint32_t len = Read<uint32_t>();
str.resize(len);
Read(reinterpret_cast<uint8_t*>(&str[0]), len);
return str;
}
@ -49,9 +49,8 @@ std::string ByteStream::Read() {
template <>
std::u16string ByteStream::Read() {
std::u16string str;
uint32_t len = Read<uint32_t>();
size_t len = Read<uint32_t>();
str.resize(len);
Read(reinterpret_cast<uint8_t*>(&str[0]), len * 2);
return str;
}

View File

@ -2,41 +2,51 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2019 Ben Vanik. All rights reserved. *
* Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/base/clock.h"
#include <sys/time.h>
#include <time.h>
#include "xenia/base/assert.h"
#include "xenia/base/clock.h"
namespace xe {
uint64_t Clock::host_tick_frequency_platform() {
timespec res;
clock_getres(CLOCK_MONOTONIC_RAW, &res);
int error = clock_getres(CLOCK_MONOTONIC_RAW, &res);
assert_zero(error);
assert_zero(res.tv_sec); // Sub second resolution is required.
return uint64_t(res.tv_sec) + uint64_t(res.tv_nsec) * 1000000000ull;
// Convert nano seconds to hertz. Resolution is 1ns on most systems.
return 1000000000ull / res.tv_nsec;
}
uint64_t Clock::host_tick_count_platform() {
timespec res;
clock_gettime(CLOCK_MONOTONIC_RAW, &res);
timespec tp;
int error = clock_gettime(CLOCK_MONOTONIC_RAW, &tp);
assert_zero(error);
return uint64_t(res.tv_sec) + uint64_t(res.tv_nsec) * 1000000000ull;
return tp.tv_nsec + tp.tv_sec * 1000000000ull;
}
uint64_t Clock::QueryHostSystemTime() {
struct timeval tv;
gettimeofday(&tv, NULL);
// https://docs.microsoft.com/en-us/windows/win32/sysinfo/converting-a-time-t-value-to-a-file-time
constexpr uint64_t seconds_per_day = 3600 * 24;
// Don't forget the 89 leap days.
constexpr uint64_t seconds_1601_to_1970 =
((369 * 365 + 89) * seconds_per_day);
uint64_t ret = tv.tv_usec;
ret /= 1000; // usec -> msec
timeval now;
int error = gettimeofday(&now, nullptr);
assert_zero(error);
ret += (tv.tv_sec * 1000); // sec -> msec
return ret;
// NT systems use 100ns intervals.
return static_cast<uint64_t>(
(static_cast<int64_t>(now.tv_sec) + seconds_1601_to_1970) * 10000000ull +
now.tv_usec * 10);
}
uint64_t Clock::QueryHostUptimeMillis() {

View File

@ -14,6 +14,10 @@
#define UTF_CPP_CPLUSPLUS 201703L
#include "third_party/utfcpp/source/utf8.h"
#include "xenia/base/logging.h"
#include "xenia/base/main.h"
#include "xenia/base/system.h"
namespace utfcpp = utf8;
using u8_citer = utfcpp::iterator<std::string_view::const_iterator>;
@ -61,7 +65,14 @@ void ParseLaunchArguments(int& argc, char**& argv,
auto result = options.parse(argc, argv);
if (result.count("help")) {
PrintHelpAndExit();
xe::AttachConsole();
if (xe::has_console_attached()) {
PrintHelpAndExit();
} else {
xe::ShowSimpleMessageBox(xe::SimpleMessageBoxType::Help,
options.help({""}));
exit(0);
}
}
for (auto& it : *CmdVars) {
@ -78,8 +89,16 @@ void ParseLaunchArguments(int& argc, char**& argv,
}
}
} catch (const cxxopts::OptionException& e) {
std::cout << e.what() << std::endl;
PrintHelpAndExit();
xe::AttachConsole();
if (xe::has_console_attached()) {
std::cout << e.what() << std::endl;
PrintHelpAndExit();
} else {
std::string m =
"Invalid launch options were given.\n" + options.help({""});
xe::ShowSimpleMessageBox(xe::SimpleMessageBoxType::Error, m);
exit(0);
}
}
}

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<!-- Automatically convert endianness for xe::be -->
<Type Name="xe::be&lt;unsigned __int64&gt;">
<!-- Automatically convert endianness for xe::endian_store<,1> -->
<Type Name="xe::endian_store&lt;unsigned __int64, 1&gt;">
<DisplayString>
{(((value &amp; 0xFF00000000000000) &gt;&gt; 56) |
((value &amp; 0x00FF000000000000) &gt;&gt; 40) |
@ -14,7 +14,7 @@
((value &amp; 0x00000000000000FF) &lt;&lt; 56))}
</DisplayString>
</Type>
<Type Name="xe::be&lt;__int64&gt;">
<Type Name="xe::endian_store&lt;__int64, 1&gt;">
<DisplayString>
{(((value &amp; 0xFF00000000000000) &gt;&gt; 56) |
((value &amp; 0x00FF000000000000) &gt;&gt; 40) |
@ -27,7 +27,7 @@
</DisplayString>
</Type>
<Type Name="xe::be&lt;unsigned int&gt;">
<Type Name="xe::endian_store&lt;unsigned int, 1&gt;">
<DisplayString>
{(((value &amp; 0xFF000000) &gt;&gt; 24) |
((value &amp; 0x00FF0000) &gt;&gt; 8) |
@ -35,7 +35,7 @@
((value &amp; 0x000000FF) &lt;&lt; 24))}
</DisplayString>
</Type>
<Type Name="xe::be&lt;int&gt;">
<Type Name="xe::endian_store&lt;int, 1&gt;">
<DisplayString>
{(((value &amp; 0xFF000000) &gt;&gt; 24) |
((value &amp; 0x00FF0000) &gt;&gt; 8) |
@ -44,25 +44,25 @@
</DisplayString>
</Type>
<Type Name="xe::be&lt;unsigned short&gt;">
<Type Name="xe::endian_store&lt;unsigned short, 1&gt;">
<DisplayString>
{(((value &amp; 0xFF00) &gt;&gt; 8) |
((value &amp; 0x00FF) &lt;&lt; 8))}
</DisplayString>
</Type>
<Type Name="xe::be&lt;short&gt;">
<Type Name="xe::endian_store&lt;short, 1&gt;">
<DisplayString>
{(((value &amp; 0xFF00) &gt;&gt; 8) |
((value &amp; 0x00FF) &lt;&lt; 8))}
</DisplayString>
</Type>
<Type Name="xe::be&lt;unsigned char&gt;">
<Type Name="xe::endian_store&lt;unsigned char, 1&gt;">
<DisplayString>
{value}
</DisplayString>
</Type>
<Type Name="xe::be&lt;char&gt;">
<Type Name="xe::endian_store&lt;char, 1&gt;">
<DisplayString>
{value}
</DisplayString>

View File

@ -26,6 +26,7 @@
#include "xenia/base/memory.h"
#include "xenia/base/ring_buffer.h"
#include "xenia/base/string.h"
#include "xenia/base/system.h"
#include "xenia/base/threading.h"
// For MessageBox:
@ -58,7 +59,7 @@ struct LogLine {
size_t buffer_length;
uint32_t thread_id;
uint16_t _pad_0; // (2b) padding
uint8_t _pad_1; // (1b) padding
bool terminate;
char prefix_char;
};
@ -81,8 +82,7 @@ class Logger {
explicit Logger(const std::string_view app_name)
: wait_strategy_(),
claim_strategy_(kBlockCount, wait_strategy_),
consumed_(wait_strategy_),
running_(true) {
consumed_(wait_strategy_) {
claim_strategy_.add_claim_barrier(consumed_);
write_thread_ =
@ -91,7 +91,7 @@ class Logger {
}
~Logger() {
running_ = false;
AppendLine(0, '\0', nullptr, 0, true); // append a terminator
xe::threading::Wait(write_thread_.get(), true);
}
@ -124,7 +124,6 @@ class Logger {
std::vector<std::unique_ptr<LogSink>> sinks_;
std::atomic<bool> running_;
std::unique_ptr<xe::threading::Thread> write_thread_;
void Write(const char* buf, size_t size) {
@ -153,35 +152,30 @@ class Logger {
auto available_sequence = claim_strategy_.wait_until_published(
next_range.last(), last_sequence);
auto available_difference =
dp::difference(available_sequence, next_sequence);
size_t read_count = 0;
auto available_range = next_range;
auto available_count = available_range.size();
if (available_difference > 0 &&
static_cast<size_t>(available_difference) >= desired_count) {
auto available_range = dp::sequence_range(
next_sequence, static_cast<size_t>(available_difference));
auto available_count = available_range.size();
rb.set_write_offset(BlockOffset(available_range.end()));
rb.set_write_offset(BlockOffset(available_range.end()));
bool terminate = false;
for (size_t i = available_range.first(); i != available_range.end();) {
rb.set_read_offset(BlockOffset(i));
for (size_t i = available_range.first(); i != available_range.end();) {
rb.set_read_offset(BlockOffset(i));
LogLine line;
rb.Read(&line, sizeof(line));
LogLine line;
rb.Read(&line, sizeof(line));
auto needed_count = BlockCount(sizeof(LogLine) + line.buffer_length);
if (read_count + needed_count > available_count) {
// More blocks are needed for a complete line.
desired_count = needed_count;
break;
} else {
// Enough blocks to read this log line, advance by that many.
read_count += needed_count;
i += needed_count;
auto needed_count = BlockCount(sizeof(LogLine) + line.buffer_length);
if (read_count + needed_count > available_count) {
// More blocks are needed for a complete line.
desired_count = needed_count;
break;
} else {
// Enough blocks to read this log line, advance by that many.
read_count += needed_count;
i += needed_count;
if (line.prefix_char) {
char prefix[] = {
line.prefix_char,
'>',
@ -200,44 +194,53 @@ class Logger {
fmt::format_to_n(prefix + 3, sizeof(prefix) - 3, "{:08X}",
line.thread_id);
Write(prefix, sizeof(prefix) - 1);
}
if (line.buffer_length) {
// Get access to the line data - which may be split in the ring
// buffer - and write it out in parts.
auto line_range = rb.BeginRead(line.buffer_length);
Write(reinterpret_cast<const char*>(line_range.first),
line_range.first_length);
if (line_range.second_length) {
Write(reinterpret_cast<const char*>(line_range.second),
line_range.second_length);
}
if (line.buffer_length) {
// Get access to the line data - which may be split in the ring
// buffer - and write it out in parts.
auto line_range = rb.BeginRead(line.buffer_length);
Write(reinterpret_cast<const char*>(line_range.first),
line_range.first_length);
if (line_range.second_length) {
Write(reinterpret_cast<const char*>(line_range.second),
line_range.second_length);
}
// Always ensure there is a newline.
char last_char =
line_range.second
? line_range.second[line_range.second_length - 1]
: line_range.first[line_range.first_length - 1];
if (last_char != '\n') {
const char suffix[1] = {'\n'};
Write(suffix, 1);
}
rb.EndRead(std::move(line_range));
} else {
// Always ensure there is a newline.
// Always ensure there is a newline.
char last_char =
line_range.second
? line_range.second[line_range.second_length - 1]
: line_range.first[line_range.first_length - 1];
if (last_char != '\n') {
const char suffix[1] = {'\n'};
Write(suffix, 1);
}
rb.EndRead(std::move(line_range));
} else {
// Always ensure there is a newline.
const char suffix[1] = {'\n'};
Write(suffix, 1);
}
if (line.terminate) {
terminate = true;
break;
}
}
}
if (terminate) {
break;
}
if (read_count) {
// Advance by the number of blocks we read.
auto read_range = dp::sequence_range(next_sequence, read_count);
next_sequence = read_range.end();
last_sequence = read_range.last();
consumed_.publish(read_range.last());
consumed_.publish(last_sequence);
desired_count = 1;
@ -249,9 +252,6 @@ class Logger {
idle_loops = 0;
} else {
if (!running_) {
break;
}
if (idle_loops >= 1000) {
// Introduce a waiting period.
xe::threading::Sleep(std::chrono::milliseconds(50));
@ -264,7 +264,8 @@ class Logger {
public:
void AppendLine(uint32_t thread_id, const char prefix_char,
const char* buffer_data, size_t buffer_length) {
const char* buffer_data, size_t buffer_length,
bool terminate = false) {
size_t count = BlockCount(sizeof(LogLine) + buffer_length);
auto range = claim_strategy_.claim(count);
@ -278,9 +279,12 @@ class Logger {
line.buffer_length = buffer_length;
line.thread_id = thread_id;
line.prefix_char = prefix_char;
line.terminate = terminate;
rb.Write(&line, sizeof(LogLine));
rb.Write(buffer_data, buffer_length);
if (buffer_length) {
rb.Write(buffer_data, buffer_length);
}
claim_strategy_.publish(range);
}
@ -350,12 +354,10 @@ void logging::AppendLogLine(LogLevel log_level, const char prefix_char,
void FatalError(const std::string_view str) {
logging::AppendLogLine(LogLevel::Error, 'X', str);
#if XE_PLATFORM_WIN32
if (!xe::has_console_attached()) {
MessageBoxW(NULL, (LPCWSTR)xe::to_utf16(str).c_str(), L"Xenia Error",
MB_OK | MB_ICONERROR | MB_APPLMODAL | MB_SETFOREGROUND);
ShowSimpleMessageBox(SimpleMessageBoxType::Error, str);
}
#endif // WIN32
ShutdownLogging();
std::exit(1);
}

View File

@ -10,6 +10,7 @@
#ifndef XENIA_BASE_MAIN_H_
#define XENIA_BASE_MAIN_H_
#include <optional>
#include <string>
#include <vector>
@ -21,23 +22,32 @@ namespace xe {
// Returns true if there is a user-visible console attached to receive stdout.
bool has_console_attached();
void AttachConsole();
// Extern defined by user code. This must be present for the application to
// launch.
struct EntryInfo {
std::string name;
std::string positional_usage;
std::vector<std::string> positional_options;
int (*entry_point)(const std::vector<std::string>& args);
bool transparent_options; // no argument parsing
std::optional<std::string> positional_usage;
std::optional<std::vector<std::string>> positional_options;
};
EntryInfo GetEntryInfo();
#define DEFINE_ENTRY_POINT(name, entry_point, positional_usage, ...) \
xe::EntryInfo xe::GetEntryInfo() { \
std::initializer_list<std::string> positional_options = {__VA_ARGS__}; \
return xe::EntryInfo( \
{name, positional_usage, \
std::vector<std::string>(std::move(positional_options)), \
entry_point}); \
return xe::EntryInfo{ \
name, entry_point, false, positional_usage, \
std::vector<std::string>(std::move(positional_options))}; \
}
// TODO(Joel Linn): Add some way to filter consumed arguments in
// cvar::ParseLaunchArguments()
#define DEFINE_ENTRY_POINT_TRANSPARENT(name, entry_point) \
xe::EntryInfo xe::GetEntryInfo() { \
return xe::EntryInfo{name, entry_point, true, std::nullopt, std::nullopt}; \
}
} // namespace xe

View File

@ -2,11 +2,14 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <stdio.h>
#include <unistd.h>
#include "xenia/base/cvar.h"
#include "xenia/base/main.h"
@ -16,15 +19,19 @@
namespace xe {
bool has_console_attached() { return true; }
bool has_console_attached() { return isatty(fileno(stdin)) == 1; }
void AttachConsole() {}
} // namespace xe
extern "C" int main(int argc, char** argv) {
auto entry_info = xe::GetEntryInfo();
cvar::ParseLaunchArguments(argc, argv, entry_info.positional_usage,
entry_info.positional_options);
if (!entry_info.transparent_options) {
cvar::ParseLaunchArguments(argc, argv, entry_info.positional_usage.value(),
entry_info.positional_options.value());
}
std::vector<std::string> args;
for (int n = 0; n < argc; n++) {

View File

@ -2,7 +2,7 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
@ -38,8 +38,22 @@ bool has_console_attached_ = true;
bool has_console_attached() { return has_console_attached_; }
bool has_shell_environment_variable() {
size_t size = 0;
// Check if SHELL exists
// If it doesn't, then we are in a Windows Terminal
auto error = getenv_s(&size, nullptr, 0, "SHELL");
if (error) {
return false;
}
return !!size;
}
void AttachConsole() {
if (!cvars::enable_console) {
bool has_console = ::AttachConsole(ATTACH_PARENT_PROCESS) == TRUE;
if (!has_console || !has_shell_environment_variable()) {
// We weren't launched from a console, so just return.
has_console_attached_ = false;
return;
}
@ -104,8 +118,10 @@ static bool parse_launch_arguments(const xe::EntryInfo& entry_info,
LocalFree(wargv);
cvar::ParseLaunchArguments(argc, argv, entry_info.positional_usage,
entry_info.positional_options);
if (!entry_info.transparent_options) {
cvar::ParseLaunchArguments(argc, argv, entry_info.positional_usage.value(),
entry_info.positional_options.value());
}
args.clear();
for (int n = 0; n < argc; n++) {
@ -125,7 +141,9 @@ int Main() {
// Attach a console so we can write output to stdout. If the user hasn't
// redirected output themselves it'll pop up a window.
xe::AttachConsole();
if (cvars::enable_console) {
xe::AttachConsole();
}
// Setup COM on the main thread.
// NOTE: this may fail if COM has already been initialized - that's OK.
@ -144,7 +162,13 @@ int Main() {
}
// Print version info.
XELOGI("Build: " XE_BUILD_BRANCH " / " XE_BUILD_COMMIT " on " XE_BUILD_DATE);
XELOGI(
"Build: "
#ifdef XE_BUILD_IS_PR
"PR#" XE_BUILD_PR_NUMBER " " XE_BUILD_PR_REPO " " XE_BUILD_PR_BRANCH
"@" XE_BUILD_PR_COMMIT_SHORT " against "
#endif
XE_BUILD_BRANCH "@" XE_BUILD_COMMIT_SHORT " on " XE_BUILD_DATE);
// Request high performance timing.
if (cvars::win32_high_freq) {

View File

@ -17,6 +17,16 @@
#include <limits>
#include <numeric>
#include <type_traits>
#if defined __has_include
#if __has_include(<version>)
#include <version>
#endif
#endif
#if __cpp_lib_bitops
#include <bit>
#endif
#include "xenia/base/platform.h"
#if XE_ARCH_AMD64
@ -47,8 +57,20 @@ constexpr T round_up(T value, V multiple) {
return value ? (((value + multiple - 1) / multiple) * multiple) : multiple;
}
constexpr float saturate(float value) {
return std::max(std::min(1.0f, value), -1.0f);
// Using the same conventions as in shading languages, returning 0 for NaN.
// std::max is `a < b ? b : a`, thus in case of NaN, the first argument is
// always returned. Also -0 is not < +0, so +0 is also chosen for it.
template <typename T>
constexpr T saturate_unsigned(T value) {
return std::min(static_cast<T>(1.0f), std::max(static_cast<T>(0.0f), value));
}
// This diverges from the GPU NaN rules for signed normalized formats (NaN
// should be converted to 0, not to -1), but this expectation is not needed most
// of time, and cannot be met for free (unlike for 0...1 clamping).
template <typename T>
constexpr T saturate_signed(T value) {
return std::min(static_cast<T>(1.0f), std::max(static_cast<T>(-1.0f), value));
}
// Gets the next power of two value that is greater than or equal to the given
@ -101,6 +123,23 @@ constexpr uint32_t select_bits(uint32_t value, uint32_t a, uint32_t b) {
return (value & make_bitmask(a, b)) >> a;
}
#if __cpp_lib_bitops
template <class T>
constexpr inline uint32_t bit_count(T v) {
return static_cast<uint32_t>(std::popcount(v));
}
#else
#if XE_COMPILER_MSVC || XE_COMPILER_INTEL
inline uint32_t bit_count(uint32_t v) { return __popcnt(v); }
inline uint32_t bit_count(uint64_t v) {
return static_cast<uint32_t>(__popcnt64(v));
}
#elif XE_COMPILER_GCC || XE_COMPILER_CLANG
static_assert(sizeof(unsigned int) == sizeof(uint32_t));
static_assert(sizeof(unsigned long long) == sizeof(uint64_t));
inline uint32_t bit_count(uint32_t v) { return __builtin_popcount(v); }
inline uint32_t bit_count(uint64_t v) { return __builtin_popcountll(v); }
#else
inline uint32_t bit_count(uint32_t v) {
v = v - ((v >> 1) & 0x55555555);
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
@ -116,6 +155,8 @@ inline uint32_t bit_count(uint64_t v) {
v = v + (v >> 32) & 0x0000007F;
return static_cast<uint32_t>(v);
}
#endif
#endif
// lzcnt instruction, typed for integers of all sizes.
// The number of leading zero bits in the value parameter. If value is zero, the

View File

@ -43,6 +43,16 @@ void copy_128_aligned(void* dest, const void* src, size_t count) {
}
#if XE_ARCH_AMD64
// This works around a GCC bug
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100801
// TODO(Joel Linn): Remove this when fixed GCC versions are common place.
#if XE_COMPILER_GNUC
#define XE_WORKAROUND_LOOP_KILL_MOD(x) \
if ((count % (x)) == 0) __builtin_unreachable();
#else
#define XE_WORKAROUND_LOOP_KILL_MOD(x)
#endif
void copy_and_swap_16_aligned(void* dest_ptr, const void* src_ptr,
size_t count) {
assert_zero(reinterpret_cast<uintptr_t>(dest_ptr) & 0xF);
@ -61,6 +71,7 @@ void copy_and_swap_16_aligned(void* dest_ptr, const void* src_ptr,
_mm_store_si128(reinterpret_cast<__m128i*>(&dest[i]), output);
}
for (; i < count; ++i) { // handle residual elements
XE_WORKAROUND_LOOP_KILL_MOD(8);
dest[i] = byte_swap(src[i]);
}
}
@ -80,6 +91,7 @@ void copy_and_swap_16_unaligned(void* dest_ptr, const void* src_ptr,
_mm_storeu_si128(reinterpret_cast<__m128i*>(&dest[i]), output);
}
for (; i < count; ++i) { // handle residual elements
XE_WORKAROUND_LOOP_KILL_MOD(8);
dest[i] = byte_swap(src[i]);
}
}
@ -102,6 +114,7 @@ void copy_and_swap_32_aligned(void* dest_ptr, const void* src_ptr,
_mm_store_si128(reinterpret_cast<__m128i*>(&dest[i]), output);
}
for (; i < count; ++i) { // handle residual elements
XE_WORKAROUND_LOOP_KILL_MOD(4);
dest[i] = byte_swap(src[i]);
}
}
@ -121,6 +134,7 @@ void copy_and_swap_32_unaligned(void* dest_ptr, const void* src_ptr,
_mm_storeu_si128(reinterpret_cast<__m128i*>(&dest[i]), output);
}
for (; i < count; ++i) { // handle residual elements
XE_WORKAROUND_LOOP_KILL_MOD(4);
dest[i] = byte_swap(src[i]);
}
}
@ -143,6 +157,7 @@ void copy_and_swap_64_aligned(void* dest_ptr, const void* src_ptr,
_mm_store_si128(reinterpret_cast<__m128i*>(&dest[i]), output);
}
for (; i < count; ++i) { // handle residual elements
XE_WORKAROUND_LOOP_KILL_MOD(2);
dest[i] = byte_swap(src[i]);
}
}
@ -162,6 +177,7 @@ void copy_and_swap_64_unaligned(void* dest_ptr, const void* src_ptr,
_mm_storeu_si128(reinterpret_cast<__m128i*>(&dest[i]), output);
}
for (; i < count; ++i) { // handle residual elements
XE_WORKAROUND_LOOP_KILL_MOD(2);
dest[i] = byte_swap(src[i]);
}
}
@ -178,6 +194,7 @@ void copy_and_swap_16_in_32_aligned(void* dest_ptr, const void* src_ptr,
_mm_store_si128(reinterpret_cast<__m128i*>(&dest[i]), output);
}
for (; i < count; ++i) { // handle residual elements
XE_WORKAROUND_LOOP_KILL_MOD(4);
dest[i] = (src[i] >> 16) | (src[i] << 16);
}
}
@ -194,6 +211,7 @@ void copy_and_swap_16_in_32_unaligned(void* dest_ptr, const void* src_ptr,
_mm_storeu_si128(reinterpret_cast<__m128i*>(&dest[i]), output);
}
for (; i < count; ++i) { // handle residual elements
XE_WORKAROUND_LOOP_KILL_MOD(4);
dest[i] = (src[i] >> 16) | (src[i] << 16);
}
}

View File

@ -15,6 +15,7 @@
#include <filesystem>
#include <functional>
#include <string>
#include <string_view>
#include "xenia/base/assert.h"
#include "xenia/base/byte_order.h"
@ -441,6 +442,26 @@ inline void store_and_swap<std::u16string>(void* mem,
return store_and_swap<std::u16string_view>(mem, value);
}
using fourcc_t = uint32_t;
// Get FourCC in host byte order
// make_fourcc('a', 'b', 'c', 'd') == 0x61626364
constexpr inline fourcc_t make_fourcc(char a, char b, char c, char d) {
return fourcc_t((static_cast<fourcc_t>(a) << 24) |
(static_cast<fourcc_t>(b) << 16) |
(static_cast<fourcc_t>(c) << 8) | static_cast<fourcc_t>(d));
}
// Get FourCC in host byte order
// This overload requires fourcc.length() == 4
// make_fourcc("abcd") == 'abcd' == 0x61626364 for most compilers
constexpr inline fourcc_t make_fourcc(const std::string_view fourcc) {
if (fourcc.length() != 4) {
throw std::runtime_error("Invalid fourcc length");
}
return make_fourcc(fourcc[0], fourcc[1], fourcc[2], fourcc[3]);
}
} // namespace xe
#endif // XENIA_BASE_MEMORY_H_

View File

@ -85,18 +85,17 @@
#endif // XE_PLATFORM_MAC
#if XE_COMPILER_MSVC
#define XEPACKEDSTRUCT(name, value) \
__pragma(pack(push, 1)) struct name value __pragma(pack(pop));
#define XEPACKEDSTRUCTANONYMOUS(value) \
__pragma(pack(push, 1)) struct value __pragma(pack(pop));
#define XEPACKEDUNION(name, value) \
__pragma(pack(push, 1)) union name value __pragma(pack(pop));
#define _XEPACKEDSCOPE(body) __pragma(pack(push, 1)) body __pragma(pack(pop));
#else
#define XEPACKEDSTRUCT(name, value) struct __attribute__((packed)) name value;
#define XEPACKEDSTRUCTANONYMOUS(value) struct __attribute__((packed)) value;
#define XEPACKEDUNION(name, value) union __attribute__((packed)) name value;
#define _XEPACKEDSCOPE(body) \
_Pragma("pack(push, 1)") body; \
_Pragma("pack(pop)");
#endif // XE_PLATFORM_WIN32
#define XEPACKEDSTRUCT(name, value) _XEPACKEDSCOPE(struct name value)
#define XEPACKEDSTRUCTANONYMOUS(value) _XEPACKEDSCOPE(struct value)
#define XEPACKEDUNION(name, value) _XEPACKEDSCOPE(union name value)
namespace xe {
#if XE_PLATFORM_WIN32

View File

@ -30,6 +30,7 @@
#include "xenia/base/assert.h"
#include "xenia/base/cvar.h"
#include "xenia/base/profiling.h"
#include "xenia/ui/virtual_key.h"
#include "xenia/ui/window.h"
#if XE_OPTION_PROFILING
@ -112,31 +113,35 @@ void Profiler::ThreadEnter(const char* name) {
void Profiler::ThreadExit() { MicroProfileOnThreadExit(); }
bool Profiler::OnKeyDown(int key_code) {
bool Profiler::OnKeyDown(ui::VirtualKey virtual_key) {
// https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
switch (key_code) {
case VK_OEM_3: // `
switch (virtual_key) {
case ui::VirtualKey::kOem3: // `
MicroProfileTogglePause();
return true;
#if XE_OPTION_PROFILING_UI
case VK_TAB:
case ui::VirtualKey::kTab:
MicroProfileToggleDisplayMode();
return true;
case 0x31: // 1
case ui::VirtualKey::k1:
MicroProfileModKey(1);
return true;
#endif // XE_OPTION_PROFILING_UI
default:
break;
}
return false;
}
bool Profiler::OnKeyUp(int key_code) {
switch (key_code) {
bool Profiler::OnKeyUp(ui::VirtualKey virtual_key) {
switch (virtual_key) {
#if XE_OPTION_PROFILING_UI
case 0x31: // 1
case ui::VirtualKey::k1:
MicroProfileModKey(0);
return true;
#endif // XE_OPTION_PROFILING_UI
default:
break;
}
return false;
}
@ -219,14 +224,14 @@ void Profiler::set_window(ui::Window* window) {
// Watch for toggle/mode keys and such.
window_->on_key_down.AddListener([](ui::KeyEvent* e) {
if (Profiler::is_visible()) {
Profiler::OnKeyDown(e->key_code());
Profiler::OnKeyDown(e->virtual_key());
e->set_handled(true);
window_->Invalidate();
}
});
window_->on_key_up.AddListener([](ui::KeyEvent* e) {
if (Profiler::is_visible()) {
Profiler::OnKeyUp(e->key_code());
Profiler::OnKeyUp(e->virtual_key());
e->set_handled(true);
window_->Invalidate();
}
@ -257,8 +262,8 @@ void Profiler::Shutdown() {}
uint32_t Profiler::GetColor(const char* str) { return 0; }
void Profiler::ThreadEnter(const char* name) {}
void Profiler::ThreadExit() {}
bool Profiler::OnKeyDown(int key_code) { return false; }
bool Profiler::OnKeyUp(int key_code) { return false; }
bool Profiler::OnKeyDown(ui::VirtualKey virtual_key) { return false; }
bool Profiler::OnKeyUp(ui::VirtualKey virtual_key) { return false; }
void Profiler::OnMouseDown(bool left_button, bool right_button) {}
void Profiler::OnMouseUp() {}
void Profiler::OnMouseMove(int x, int y) {}

View File

@ -13,6 +13,7 @@
#include <memory>
#include "xenia/base/string.h"
#include "xenia/ui/virtual_key.h"
#if XE_PLATFORM_WIN32
#define XE_OPTION_PROFILING 1
@ -171,8 +172,8 @@ class Profiler {
// Deactivates the calling thread for profiling.
static void ThreadExit();
static bool OnKeyDown(int key_code);
static bool OnKeyUp(int key_code);
static bool OnKeyDown(ui::VirtualKey virtual_key);
static bool OnKeyUp(ui::VirtualKey virtual_key);
static void OnMouseDown(bool left_button, bool right_button);
static void OnMouseUp();
static void OnMouseMove(int x, int y);

View File

@ -87,12 +87,12 @@ struct string_key_case : internal::string_key_base {
namespace std {
template <>
struct std::hash<xe::string_key> {
struct hash<xe::string_key> {
std::size_t operator()(const xe::string_key& t) const { return t.hash(); }
};
template <>
struct std::hash<xe::string_key_case> {
struct hash<xe::string_key_case> {
std::size_t operator()(const xe::string_key_case& t) const {
return t.hash();
}

View File

@ -221,7 +221,7 @@ inline T fpfs(const std::string_view value, bool force_hex) {
} else {
#if XE_COMPILER_CLANG || XE_COMPILER_GNUC
auto temp = std::string(range);
result = std::strtof(temp.c_str(), nullptr);
result = std::strtod(temp.c_str(), nullptr);
#else
auto [p, error] = std::from_chars(range.data(), range.data() + range.size(),
result, std::chars_format::general);

View File

@ -11,15 +11,24 @@
#define XENIA_BASE_SYSTEM_H_
#include <filesystem>
#include <string>
#include <string_view>
#include "xenia/base/string.h"
namespace xe {
void LaunchWebBrowser(const std::string& url);
void LaunchWebBrowser(const std::string_view url);
void LaunchFileExplorer(const std::filesystem::path& path);
enum class SimpleMessageBoxType {
Help,
Warning,
Error,
};
// This is expected to block the caller until the message box is closed.
void ShowSimpleMessageBox(SimpleMessageBoxType type, std::string_view message);
} // namespace xe
#endif // XENIA_BASE_SYSTEM_H_

View File

@ -7,22 +7,64 @@
******************************************************************************
*/
#include <alloca.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <string>
#include <cstring>
#include "xenia/base/assert.h"
#include "xenia/base/platform_linux.h"
#include "xenia/base/logging.h"
#include "xenia/base/string.h"
#include "xenia/base/system.h"
// Use headers in third party to not depend on system sdl headers for building
#include "third_party/SDL2/include/SDL.h"
namespace xe {
void LaunchWebBrowser(const std::string& url) {
auto cmd = "xdg-open " + url;
void LaunchWebBrowser(const std::string_view url) {
auto cmd = std::string("xdg-open ");
cmd.append(url);
system(cmd.c_str());
}
void LaunchFileExplorer(const std::filesystem::path& path) { assert_always(); }
void ShowSimpleMessageBox(SimpleMessageBoxType type, std::string_view message) {
void* libsdl2 = dlopen("libSDL2.so", RTLD_LAZY | RTLD_LOCAL);
assert_not_null(libsdl2);
if (libsdl2) {
auto* pSDL_ShowSimpleMessageBox =
reinterpret_cast<decltype(SDL_ShowSimpleMessageBox)*>(
dlsym(libsdl2, "SDL_ShowSimpleMessageBox"));
assert_not_null(pSDL_ShowSimpleMessageBox);
if (pSDL_ShowSimpleMessageBox) {
Uint32 flags;
const char* title;
char* message_copy = reinterpret_cast<char*>(alloca(message.size() + 1));
std::memcpy(message_copy, message.data(), message.size());
message_copy[message.size()] = '\0';
switch (type) {
default:
case SimpleMessageBoxType::Help:
title = "Xenia Help";
flags = SDL_MESSAGEBOX_INFORMATION;
break;
case SimpleMessageBoxType::Warning:
title = "Xenia Warning";
flags = SDL_MESSAGEBOX_WARNING;
break;
case SimpleMessageBoxType::Error:
title = "Xenia Error";
flags = SDL_MESSAGEBOX_ERROR;
break;
}
pSDL_ShowSimpleMessageBox(flags, title, message_copy, NULL);
}
dlclose(libsdl2);
}
}
} // namespace xe

View File

@ -13,9 +13,9 @@
namespace xe {
void LaunchWebBrowser(const std::string& url) {
auto temp = xe::to_utf16(url);
ShellExecuteW(nullptr, L"open", reinterpret_cast<LPCWSTR>(temp.c_str()),
void LaunchWebBrowser(const std::string_view url) {
auto wide_url = xe::to_utf16(url);
ShellExecuteW(nullptr, L"open", reinterpret_cast<LPCWSTR>(wide_url.c_str()),
nullptr, nullptr, SW_SHOWNORMAL);
}
@ -24,4 +24,28 @@ void LaunchFileExplorer(const std::filesystem::path& url) {
SW_SHOWNORMAL);
}
void ShowSimpleMessageBox(SimpleMessageBoxType type,
const std::string_view message) {
const wchar_t* title;
std::u16string wide_message = xe::to_utf16(message);
DWORD type_flags = MB_OK | MB_APPLMODAL | MB_SETFOREGROUND;
switch (type) {
default:
case SimpleMessageBoxType::Help:
title = L"Xenia Help";
type_flags |= MB_ICONINFORMATION;
break;
case SimpleMessageBoxType::Warning:
title = L"Xenia Warning";
type_flags |= MB_ICONWARNING;
break;
case SimpleMessageBoxType::Error:
title = L"Xenia Error";
type_flags |= MB_ICONERROR;
break;
}
MessageBoxW(nullptr, reinterpret_cast<LPCWSTR>(wide_message.c_str()), title,
type_flags);
}
} // namespace xe

View File

@ -2,7 +2,7 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
@ -18,7 +18,7 @@ namespace xe {
namespace base {
namespace test {
TEST_CASE("copy_128_aligned", "Copy and Swap") {
TEST_CASE("copy_128_aligned", "[copy_and_swap]") {
alignas(128) uint8_t src[256], dest[256];
for (uint8_t i = 0; i < 255; ++i) {
src[i] = 255 - i;
@ -37,7 +37,7 @@ TEST_CASE("copy_128_aligned", "Copy and Swap") {
REQUIRE(std::memcmp(dest, src + 1, 128));
}
TEST_CASE("copy_and_swap_16_aligned", "Copy and Swap") {
TEST_CASE("copy_and_swap_16_aligned", "[copy_and_swap]") {
alignas(16) uint16_t a = 0x1111, b = 0xABCD;
copy_and_swap_16_aligned(&a, &b, 1);
REQUIRE(a == 0xCDAB);
@ -93,7 +93,7 @@ TEST_CASE("copy_and_swap_16_aligned", "Copy and Swap") {
REQUIRE(std::strcmp(f, "s atdnra dlagimnne.t") == 0);
}
TEST_CASE("copy_and_swap_16_unaligned", "Copy and Swap") {
TEST_CASE("copy_and_swap_16_unaligned", "[copy_and_swap]") {
uint16_t a = 0x1111, b = 0xABCD;
copy_and_swap_16_unaligned(&a, &b, 1);
REQUIRE(a == 0xCDAB);
@ -139,7 +139,7 @@ TEST_CASE("copy_and_swap_16_unaligned", "Copy and Swap") {
"noeg rhtnas atdnra dlagimnne.t") == 0);
}
TEST_CASE("copy_and_swap_32_aligned", "Copy and Swap") {
TEST_CASE("copy_and_swap_32_aligned", "[copy_and_swap]") {
alignas(32) uint32_t a = 0x11111111, b = 0x89ABCDEF;
copy_and_swap_32_aligned(&a, &b, 1);
REQUIRE(a == 0xEFCDAB89);
@ -195,7 +195,7 @@ TEST_CASE("copy_and_swap_32_aligned", "Copy and Swap") {
REQUIRE(std::strcmp(f, "ats radnla dmngi.tne") == 0);
}
TEST_CASE("copy_and_swap_32_unaligned", "Copy and Swap") {
TEST_CASE("copy_and_swap_32_unaligned", "[copy_and_swap]") {
uint32_t a = 0x11111111, b = 0x89ABCDEF;
copy_and_swap_32_unaligned(&a, &b, 1);
REQUIRE(a == 0xEFCDAB89);
@ -259,7 +259,7 @@ TEST_CASE("copy_and_swap_32_unaligned", "Copy and Swap") {
"regnahtats radnla dmngi.tne") == 0);
}
TEST_CASE("copy_and_swap_64_aligned", "Copy and Swap") {
TEST_CASE("copy_and_swap_64_aligned", "[copy_and_swap]") {
alignas(64) uint64_t a = 0x1111111111111111, b = 0x0123456789ABCDEF;
copy_and_swap_64_aligned(&a, &b, 1);
REQUIRE(a == 0xEFCDAB8967452301);
@ -317,7 +317,7 @@ TEST_CASE("copy_and_swap_64_aligned", "Copy and Swap") {
REQUIRE(std::strcmp(f, "radnats mngila d") == 0);
}
TEST_CASE("copy_and_swap_64_unaligned", "Copy and Swap") {
TEST_CASE("copy_and_swap_64_unaligned", "[copy_and_swap]") {
uint64_t a = 0x1111111111111111, b = 0x0123456789ABCDEF;
copy_and_swap_64_unaligned(&a, &b, 1);
REQUIRE(a == 0xEFCDAB8967452301);
@ -407,12 +407,12 @@ TEST_CASE("copy_and_swap_64_unaligned", "Copy and Swap") {
"regradnats mngila d") == 0);
}
TEST_CASE("copy_and_swap_16_in_32_aligned", "Copy and Swap") {
TEST_CASE("copy_and_swap_16_in_32_aligned", "[copy_and_swap]") {
// TODO(bwrsandman): test once properly understood.
REQUIRE(true == true);
}
TEST_CASE("copy_and_swap_16_in_32_unaligned", "Copy and Swap") {
TEST_CASE("copy_and_swap_16_in_32_unaligned", "[copy_and_swap]") {
// TODO(bwrsandman): test once properly understood.
REQUIRE(true == true);
}
@ -425,7 +425,7 @@ TEST_CASE("create_and_close_file_mapping", "Virtual Memory Mapping") {
xe::memory::CloseFileMappingHandle(memory, path);
}
TEST_CASE("map_view", "Virtual Memory Mapping") {
TEST_CASE("map_view", "[virtual_memory_mapping]") {
auto path = fmt::format("xenia_test_{}", Clock::QueryHostTickCount());
const size_t length = 0x100;
auto memory = xe::memory::CreateFileMappingHandle(
@ -442,7 +442,7 @@ TEST_CASE("map_view", "Virtual Memory Mapping") {
xe::memory::CloseFileMappingHandle(memory, path);
}
TEST_CASE("read_write_view", "Virtual Memory Mapping") {
TEST_CASE("read_write_view", "[virtual_memory_mapping]") {
const size_t length = 0x100;
auto path = fmt::format("xenia_test_{}", Clock::QueryHostTickCount());
auto memory = xe::memory::CreateFileMappingHandle(
@ -469,6 +469,40 @@ TEST_CASE("read_write_view", "Virtual Memory Mapping") {
xe::memory::CloseFileMappingHandle(memory, path);
}
TEST_CASE("make_fourcc", "[fourcc]") {
SECTION("'1234'") {
const uint32_t fourcc_host = 0x31323334;
constexpr fourcc_t fourcc_1 = make_fourcc('1', '2', '3', '4');
constexpr fourcc_t fourcc_2 = make_fourcc("1234");
REQUIRE(fourcc_1 == fourcc_host);
REQUIRE(fourcc_2 == fourcc_host);
REQUIRE(fourcc_1 == fourcc_2);
REQUIRE(fourcc_2 == fourcc_1);
}
SECTION("'ABcd'") {
const uint32_t fourcc_host = 0x41426364;
constexpr fourcc_t fourcc_1 = make_fourcc('A', 'B', 'c', 'd');
constexpr fourcc_t fourcc_2 = make_fourcc("ABcd");
REQUIRE(fourcc_1 == fourcc_host);
REQUIRE(fourcc_2 == fourcc_host);
REQUIRE(fourcc_1 == fourcc_2);
REQUIRE(fourcc_2 == fourcc_1);
}
SECTION("'XEN\\0'") {
const uint32_t fourcc_host = 0x58454E00;
constexpr fourcc_t fourcc = make_fourcc('X', 'E', 'N', '\0');
REQUIRE(fourcc == fourcc_host);
}
SECTION("length()!=4") {
REQUIRE_THROWS(make_fourcc("AB\0\0"));
REQUIRE_THROWS(make_fourcc("AB\0\0AB"));
REQUIRE_THROWS(make_fourcc("ABCDEFGH"));
}
}
} // namespace test
} // namespace base
} // namespace xe

View File

@ -2,7 +2,7 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2018 Ben Vanik. All rights reserved. *
* Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
@ -84,17 +84,17 @@ TEST_CASE("Enable process to set thread affinity") {
EnableAffinityConfiguration();
}
TEST_CASE("Yield Current Thread", "MaybeYield") {
TEST_CASE("Yield Current Thread", "[maybe_yield]") {
// Run to see if there are any errors
MaybeYield();
}
TEST_CASE("Sync with Memory Barrier", "SyncMemory") {
TEST_CASE("Sync with Memory Barrier", "[sync_memory]") {
// Run to see if there are any errors
SyncMemory();
}
TEST_CASE("Sleep Current Thread", "Sleep") {
TEST_CASE("Sleep Current Thread", "[sleep]") {
auto wait_time = 50ms;
auto start = std::chrono::steady_clock::now();
Sleep(wait_time);
@ -102,7 +102,7 @@ TEST_CASE("Sleep Current Thread", "Sleep") {
REQUIRE(duration >= wait_time);
}
TEST_CASE("Sleep Current Thread in Alertable State", "Sleep") {
TEST_CASE("Sleep Current Thread in Alertable State", "[sleep]") {
auto wait_time = 50ms;
auto start = std::chrono::steady_clock::now();
auto result = threading::AlertableSleep(wait_time);
@ -154,7 +154,7 @@ TEST_CASE("HighResolutionTimer") {
// Time the actual sleep duration
{
const auto interval = 50ms;
std::atomic<uint64_t> counter;
std::atomic<uint64_t> counter(0);
auto start = std::chrono::steady_clock::now();
auto cb = [&counter] { ++counter; };
auto pTimer = HighResolutionTimer::CreateRepeating(interval, cb);
@ -201,7 +201,7 @@ TEST_CASE("HighResolutionTimer") {
// spawned from differing threads
}
TEST_CASE("Wait on Multiple Handles", "Wait") {
TEST_CASE("Wait on Multiple Handles", "[wait]") {
auto mutant = Mutant::Create(true);
auto semaphore = Semaphore::Create(10, 10);
auto event_ = Event::CreateManualResetEvent(false);
@ -244,7 +244,7 @@ TEST_CASE("Signal and Wait") {
REQUIRE(result == WaitResult::kSuccess);
}
TEST_CASE("Wait on Event", "Event") {
TEST_CASE("Wait on Event", "[event]") {
auto evt = Event::CreateAutoResetEvent(false);
WaitResult result;
@ -262,7 +262,7 @@ TEST_CASE("Wait on Event", "Event") {
REQUIRE(result == WaitResult::kTimeout);
}
TEST_CASE("Reset Event", "Event") {
TEST_CASE("Reset Event", "[event]") {
auto evt = Event::CreateAutoResetEvent(false);
WaitResult result;
@ -283,7 +283,7 @@ TEST_CASE("Reset Event", "Event") {
REQUIRE(result == WaitResult::kSuccess);
}
TEST_CASE("Wait on Multiple Events", "Event") {
TEST_CASE("Wait on Multiple Events", "[event]") {
auto events = std::array<std::unique_ptr<Event>, 4>{
Event::CreateAutoResetEvent(false),
Event::CreateAutoResetEvent(false),
@ -348,7 +348,7 @@ TEST_CASE("Wait on Multiple Events", "Event") {
// REQUIRE(order[3] == '3');
}
TEST_CASE("Wait on Semaphore", "Semaphore") {
TEST_CASE("Wait on Semaphore", "[semaphore]") {
WaitResult result;
std::unique_ptr<Semaphore> sem;
int previous_count = 0;
@ -406,9 +406,13 @@ TEST_CASE("Wait on Semaphore", "Semaphore") {
sem = Semaphore::Create(5, 5);
Sleep(10ms);
// Occupy the semaphore with 5 threads
auto func = [&sem] {
std::atomic<int> wait_count(0);
volatile bool threads_terminate(false);
auto func = [&sem, &wait_count, &threads_terminate] {
auto res = Wait(sem.get(), false, 100ms);
Sleep(500ms);
wait_count++;
while (!threads_terminate) {
}
if (res == WaitResult::kSuccess) {
sem->Release(1, nullptr);
}
@ -417,12 +421,14 @@ TEST_CASE("Wait on Semaphore", "Semaphore") {
std::thread(func), std::thread(func), std::thread(func),
std::thread(func), std::thread(func),
};
// Give threads time to acquire semaphore
Sleep(10ms);
// Wait for threads to finish semaphore calls
while (wait_count != 5) {
}
// Attempt to acquire full semaphore with current (6th) thread
result = Wait(sem.get(), false, 20ms);
REQUIRE(result == WaitResult::kTimeout);
// Give threads time to release semaphore
threads_terminate = true;
for (auto& t : threads) {
t.join();
}
@ -444,7 +450,7 @@ TEST_CASE("Wait on Semaphore", "Semaphore") {
// REQUIRE(sem.get() == nullptr);
}
TEST_CASE("Wait on Multiple Semaphores", "Semaphore") {
TEST_CASE("Wait on Multiple Semaphores", "[semaphore]") {
WaitResult all_result;
std::pair<WaitResult, size_t> any_result;
int previous_count;
@ -501,7 +507,7 @@ TEST_CASE("Wait on Multiple Semaphores", "Semaphore") {
REQUIRE(previous_count == 4);
}
TEST_CASE("Wait on Mutant", "Mutant") {
TEST_CASE("Wait on Mutant", "[mutant]") {
WaitResult result;
std::unique_ptr<Mutant> mut;
@ -558,7 +564,7 @@ TEST_CASE("Wait on Mutant", "Mutant") {
REQUIRE(mut->Release());
}
TEST_CASE("Wait on Multiple Mutants", "Mutant") {
TEST_CASE("Wait on Multiple Mutants", "[mutant]") {
WaitResult all_result;
std::pair<WaitResult, size_t> any_result;
std::unique_ptr<Mutant> mut0, mut1;
@ -621,7 +627,7 @@ TEST_CASE("Wait on Multiple Mutants", "Mutant") {
thread2.join();
}
TEST_CASE("Wait on Timer", "Timer") {
TEST_CASE("Wait on Timer", "[timer]") {
WaitResult result;
std::unique_ptr<Timer> timer;
@ -686,7 +692,7 @@ TEST_CASE("Wait on Timer", "Timer") {
REQUIRE(result == WaitResult::kTimeout); // No more signals from repeating
}
TEST_CASE("Wait on Multiple Timers", "Timer") {
TEST_CASE("Wait on Multiple Timers", "[timer]") {
WaitResult all_result;
std::pair<WaitResult, size_t> any_result;
@ -724,13 +730,13 @@ TEST_CASE("Wait on Multiple Timers", "Timer") {
REQUIRE(any_result.second == 1);
}
TEST_CASE("Create and Trigger Timer Callbacks", "Timer") {
TEST_CASE("Create and Trigger Timer Callbacks", "[timer]") {
// TODO(bwrsandman): Check which thread performs callback and timing of
// callback
REQUIRE(true);
}
TEST_CASE("Set and Test Current Thread ID", "Thread") {
TEST_CASE("Set and Test Current Thread ID", "[thread]") {
// System ID
auto system_id = current_thread_system_id();
REQUIRE(system_id > 0);
@ -763,71 +769,76 @@ TEST_CASE("Set and Test Current Thread Name", "Thread") {
REQUIRE_NOTHROW(set_name(old_thread_name));
}
TEST_CASE("Create and Run Thread", "Thread") {
TEST_CASE("Create and Run Thread", "[thread]") {
std::unique_ptr<Thread> thread;
WaitResult result;
Thread::CreationParameters params = {};
auto func = [] { Sleep(20ms); };
// Create most basic case of thread
thread = Thread::Create(params, func);
REQUIRE(thread->native_handle() != nullptr);
REQUIRE_NOTHROW(thread->affinity_mask());
REQUIRE(thread->name().empty());
result = Wait(thread.get(), false, 50ms);
REQUIRE(result == WaitResult::kSuccess);
SECTION("Create most basic case of thread") {
thread = Thread::Create(params, func);
REQUIRE(thread->native_handle() != nullptr);
REQUIRE_NOTHROW(thread->affinity_mask());
REQUIRE(thread->name().empty());
result = Wait(thread.get(), false, 50ms);
REQUIRE(result == WaitResult::kSuccess);
}
// Add thread name
std::string new_name = "Test thread name";
thread = Thread::Create(params, func);
auto name = thread->name();
INFO(name.c_str());
REQUIRE(name.empty());
thread->set_name(new_name);
REQUIRE(thread->name() == new_name);
result = Wait(thread.get(), false, 50ms);
REQUIRE(result == WaitResult::kSuccess);
SECTION("Add thread name") {
std::string new_name = "Test thread name";
thread = Thread::Create(params, func);
auto name = thread->name();
INFO(name.c_str());
REQUIRE(name.empty());
thread->set_name(new_name);
REQUIRE(thread->name() == new_name);
result = Wait(thread.get(), false, 50ms);
REQUIRE(result == WaitResult::kSuccess);
}
// Use Terminate to end an infinitely looping thread
thread = Thread::Create(params, [] {
while (true) {
Sleep(1ms);
}
});
result = Wait(thread.get(), false, 50ms);
REQUIRE(result == WaitResult::kTimeout);
thread->Terminate(-1);
result = Wait(thread.get(), false, 50ms);
REQUIRE(result == WaitResult::kSuccess);
SECTION("Use Terminate to end an infinitely looping thread") {
thread = Thread::Create(params, [] {
while (true) {
Sleep(1ms);
}
});
result = Wait(thread.get(), false, 50ms);
REQUIRE(result == WaitResult::kTimeout);
thread->Terminate(-1);
result = Wait(thread.get(), false, 50ms);
REQUIRE(result == WaitResult::kSuccess);
}
// Call Exit from inside an infinitely looping thread
thread = Thread::Create(params, [] {
while (true) {
SECTION("Call Exit from inside an infinitely looping thread") {
thread = Thread::Create(params, [] {
Thread::Exit(-1);
}
});
result = Wait(thread.get(), false, 50ms);
REQUIRE(result == WaitResult::kSuccess);
FAIL("Function must not return");
});
result = Wait(thread.get(), false, 50ms);
REQUIRE(result == WaitResult::kSuccess);
}
// Call timeout wait on self
result = Wait(Thread::GetCurrentThread(), false, 50ms);
REQUIRE(result == WaitResult::kTimeout);
SECTION("Call timeout wait on self") {
result = Wait(Thread::GetCurrentThread(), false, 50ms);
REQUIRE(result == WaitResult::kTimeout);
}
params.stack_size = 16 * 1024 * 1024;
thread = Thread::Create(params, [] {
while (true) {
SECTION("16kb stack size") {
params.stack_size = 16 * 1024 * 1024;
thread = Thread::Create(params, [] {
Thread::Exit(-1);
}
});
REQUIRE(thread != nullptr);
result = Wait(thread.get(), false, 50ms);
REQUIRE(result == WaitResult::kSuccess);
FAIL("Function must not return");
});
REQUIRE(thread != nullptr);
result = Wait(thread.get(), false, 50ms);
REQUIRE(result == WaitResult::kSuccess);
}
// TODO(bwrsandman): Test with different priorities
// TODO(bwrsandman): Test setting and getting thread affinity
}
TEST_CASE("Test Suspending Thread", "Thread") {
TEST_CASE("Test Suspending Thread", "[thread]") {
std::unique_ptr<Thread> thread;
WaitResult result;
Thread::CreationParameters params = {};
@ -888,7 +899,7 @@ TEST_CASE("Test Suspending Thread", "Thread") {
REQUIRE(result == threading::WaitResult::kSuccess);
}
TEST_CASE("Test Thread QueueUserCallback", "Thread") {
TEST_CASE("Test Thread QueueUserCallback", "[thread]") {
std::unique_ptr<Thread> thread;
WaitResult result;
Thread::CreationParameters params = {};

View File

@ -2,7 +2,7 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
@ -194,7 +194,7 @@ struct example_results {
};
#undef TEST_EXAMPLE_RESULT
TEST_CASE("utf8::count", "UTF-8 Count") {
TEST_CASE("UTF-8 Count", "[utf8]") {
example_results<size_t> results = {};
results.Danish[0] = 88;
results.German[0] = 58;
@ -225,7 +225,7 @@ TEST_CASE("utf8::count", "UTF-8 Count") {
// TODO(gibbed): hash_fnv1a
// TODO(gibbed): hash_fnv1a_case
TEST_CASE("utf8::split", "UTF-8 Split") {
TEST_CASE("UTF-8 Split", "[utf8]") {
std::vector<std::string_view> parts;
// Danish
@ -290,17 +290,17 @@ TEST_CASE("utf8::split", "UTF-8 Split") {
// TODO(gibbed): Turkish
}
TEST_CASE("utf8::equal_z", "UTF-8 Equal Z") {
TEST_CASE("UTF-8 Equal Z", "[utf8]") {
REQUIRE(utf8::equal_z(u8"foo", u8"foo\0"));
REQUIRE_FALSE(utf8::equal_z(u8"bar", u8"baz\0"));
}
TEST_CASE("utf8::equal_case", "UTF-8 Equal Case") {
TEST_CASE("UTF-8 Equal Case", "[utf8]") {
REQUIRE(utf8::equal_case(u8"foo", u8"foo\0"));
REQUIRE_FALSE(utf8::equal_case(u8"bar", u8"baz\0"));
}
TEST_CASE("utf8::equal_case_z", "UTF-8 Equal Case Z") {
TEST_CASE("UTF-8 Equal Case Z", "[utf8]") {
REQUIRE(utf8::equal_case_z(u8"foo", u8"foo\0"));
REQUIRE_FALSE(utf8::equal_case_z(u8"bar", u8"baz\0"));
}
@ -345,7 +345,7 @@ TEST_CASE("utf8::equal_case_z", "UTF-8 Equal Case Z") {
REQUIRE(func(input_values, '\\') == output_value); \
} while (0)
TEST_CASE("utf8::join_paths", "UTF-8 Join Paths") {
TEST_CASE("UTF-8 Join Paths", "[utf8]") {
TEST_PATHS(utf8::join_paths, u8"");
TEST_PATHS(utf8::join_paths, u8"foo", u8"foo");
TEST_PATHS(utf8::join_paths, u8"foo/bar", u8"foo", u8"bar");
@ -355,7 +355,7 @@ TEST_CASE("utf8::join_paths", "UTF-8 Join Paths") {
// TODO(gibbed): join_guest_paths
TEST_CASE("utf8::fix_path_separators", "UTF-8 Fix Path Separators") {
TEST_CASE("UTF-8 Fix Path Separators", "[utf8]") {
TEST_PATH_RAW(utf8::fix_path_separators, "", "");
TEST_PATH_RAW(utf8::fix_path_separators, "\\", "/");
TEST_PATH_RAW(utf8::fix_path_separators, "/", "/");
@ -386,22 +386,47 @@ TEST_CASE("utf8::fix_path_separators", "UTF-8 Fix Path Separators") {
// TODO(gibbed): fix_guest_path_separators
TEST_CASE("utf8::find_name_from_path", "UTF-8 Find Name From Path") {
TEST_CASE("UTF-8 Find Name From Path", "[utf8]") {
TEST_PATH(utf8::find_name_from_path, "/", "");
TEST_PATH(utf8::find_name_from_path, "//", "");
TEST_PATH(utf8::find_name_from_path, "///", "");
TEST_PATH(utf8::find_name_from_path, "C/", "C");
TEST_PATH(utf8::find_name_from_path, "/C/", "C");
TEST_PATH(utf8::find_name_from_path, "C/D/", "D");
TEST_PATH(utf8::find_name_from_path, "/C/D/E/", "E");
TEST_PATH(utf8::find_name_from_path, "foo/bar/D/", "D");
TEST_PATH(utf8::find_name_from_path, "/foo/bar/E/qux/", "qux");
TEST_PATH(utf8::find_name_from_path, "foo/bar/baz/qux/", "qux");
TEST_PATH(utf8::find_name_from_path, "foo/bar/baz/qux//", "qux");
TEST_PATH(utf8::find_name_from_path, "foo/bar/baz/qux///", "qux");
TEST_PATH(utf8::find_name_from_path, "foo/bar/baz/qux.txt", "qux.txt");
TEST_PATH(utf8::find_name_from_path, "ほげ/ぴよ/ふが/ほげら/ほげほげ/",
"ほげほげ");
TEST_PATH(utf8::find_name_from_path, "ほげ/ぴよ/ふが/ほげら/ほげほげ//",
"ほげほげ");
TEST_PATH(utf8::find_name_from_path, "ほげ/ぴよ/ふが/ほげら/ほげほげ///",
"ほげほげ");
TEST_PATH(utf8::find_name_from_path, "ほげ/ぴよ/ふが/ほげら/ほげほげ.txt",
"ほげほげ.txt");
TEST_PATH(utf8::find_name_from_path, "/foo", "foo");
TEST_PATH(utf8::find_name_from_path, "//foo", "foo");
TEST_PATH(utf8::find_name_from_path, "///foo", "foo");
TEST_PATH(utf8::find_name_from_path, "/foo/bar/baz/qux.txt", "qux.txt");
TEST_PATH(utf8::find_name_from_path, "/ほげ/ぴよ/ふが/ほげら/ほげほげ/",
"ほげほげ");
TEST_PATH(utf8::find_name_from_path, "/ほげ/ぴよ/ふが/ほげら/ほげほげ//",
"ほげほげ");
TEST_PATH(utf8::find_name_from_path, "/ほげ/ぴよ/ふが/ほげら/ほげほげ///",
"ほげほげ");
TEST_PATH(utf8::find_name_from_path, "/ほげ/ぴよ/ふが/ほげら/ほげほげ.txt",
"ほげほげ.txt");
TEST_PATH(utf8::find_name_from_path, "X:/foo/bar/baz/qux.txt", "qux.txt");
TEST_PATH(utf8::find_name_from_path, "X:/ほげ/ぴよ/ふが/ほげら/ほげほげ/",
"ほげほげ");
TEST_PATH(utf8::find_name_from_path, "X:/ほげ/ぴよ/ふが/ほげら/ほげほげ//",
"ほげほげ");
TEST_PATH(utf8::find_name_from_path, "X:/ほげ/ぴよ/ふが/ほげら/ほげほげ///",
"ほげほげ");
TEST_PATH(utf8::find_name_from_path, "X:/ほげ/ぴよ/ふが/ほげら/ほげほげ.txt",
"ほげほげ.txt");
TEST_PATH(utf8::find_name_from_path, "X:/ほげ/ぴよ/ふが/ほげら.ほげほげ",
@ -410,78 +435,144 @@ TEST_CASE("utf8::find_name_from_path", "UTF-8 Find Name From Path") {
// TODO(gibbed): find_name_from_guest_path
TEST_CASE("utf8::find_base_name_from_path", "UTF-8 Find Base Name From Path") {
TEST_CASE("UTF-8 Find Base Name From Path", "[utf8]") {
TEST_PATH(utf8::find_base_name_from_path, "foo/bar/baz/qux.txt", "qux");
TEST_PATH(utf8::find_base_name_from_path, "foo/bar/baz/qux/", "qux");
TEST_PATH(utf8::find_base_name_from_path, "foo/bar/baz/qux//", "qux");
TEST_PATH(utf8::find_base_name_from_path, "foo/bar/baz/qux///", "qux");
TEST_PATH(utf8::find_base_name_from_path, "C/", "C");
TEST_PATH(utf8::find_base_name_from_path, "/C/", "C");
TEST_PATH(utf8::find_base_name_from_path, "C/D/", "D");
TEST_PATH(utf8::find_base_name_from_path, "/C/D/E/", "E");
TEST_PATH(utf8::find_base_name_from_path, "foo/bar/D/", "D");
TEST_PATH(utf8::find_base_name_from_path,
"ほげ/ぴよ/ふが/ほげら/ほげほげ.txt", "ほげほげ");
TEST_PATH(utf8::find_base_name_from_path, "ほげ/ぴよ/ふが/ほげら/ほげほげ/",
"ほげほげ");
TEST_PATH(utf8::find_base_name_from_path, "ほげ/ぴよ/ふが/ほげら/ほげほげ//",
"ほげほげ");
TEST_PATH(utf8::find_base_name_from_path, "ほげ/ぴよ/ふが/ほげら/ほげほげ///",
"ほげほげ");
TEST_PATH(utf8::find_base_name_from_path, "ほげ/ぴよ/ふが/ほげら.ほげほげ",
"ほげら");
TEST_PATH(utf8::find_base_name_from_path, "/foo/bar/baz/qux.txt", "qux");
TEST_PATH(utf8::find_base_name_from_path, "/foo/bar/baz/qux/", "qux");
TEST_PATH(utf8::find_base_name_from_path, "/foo/bar/baz/qux//", "qux");
TEST_PATH(utf8::find_base_name_from_path, "/foo/bar/baz/qux///", "qux");
TEST_PATH(utf8::find_base_name_from_path,
"/ほげ/ぴよ/ふが/ほげら/ほげほげ.txt", "ほげほげ");
TEST_PATH(utf8::find_base_name_from_path, "/ほげ/ぴよ/ふが/ほげら/ほげほげ/",
"ほげほげ");
TEST_PATH(utf8::find_base_name_from_path, "/ほげ/ぴよ/ふが/ほげら/ほげほげ//",
"ほげほげ");
TEST_PATH(utf8::find_base_name_from_path,
"/ほげ/ぴよ/ふが/ほげら/ほげほげ///", "ほげほげ");
TEST_PATH(utf8::find_base_name_from_path, "/ほげ/ぴよ/ふが/ほげら.ほげほげ",
"ほげら");
TEST_PATH(utf8::find_base_name_from_path, "X:/foo/bar/baz/qux.txt", "qux");
TEST_PATH(utf8::find_base_name_from_path, "X:/foo/bar/baz/qux/", "qux");
TEST_PATH(utf8::find_base_name_from_path, "X:/foo/bar/baz/qux//", "qux");
TEST_PATH(utf8::find_base_name_from_path, "X:/foo/bar/baz/qux///", "qux");
TEST_PATH(utf8::find_base_name_from_path,
"X:/ほげ/ぴよ/ふが/ほげら/ほげほげ.txt", "ほげほげ");
TEST_PATH(utf8::find_base_name_from_path,
"X:/ほげ/ぴよ/ふが/ほげら/ほげほげ/", "ほげほげ");
TEST_PATH(utf8::find_base_name_from_path,
"X:/ほげ/ぴよ/ふが/ほげら/ほげほげ//", "ほげほげ");
TEST_PATH(utf8::find_base_name_from_path,
"X:/ほげ/ぴよ/ふが/ほげら/ほげほげ///", "ほげほげ");
TEST_PATH(utf8::find_base_name_from_path, "X:/ほげ/ぴよ/ふが/ほげら.ほげほげ",
"ほげら");
}
// TODO(gibbed): find_base_name_from_guest_path
TEST_CASE("utf8::find_base_path", "UTF-8 Find Base Path") {
TEST_CASE("UTF-8 Find Base Path", "[utf8]") {
TEST_PATH(utf8::find_base_path, "", "");
TEST_PATH(utf8::find_base_path, "/", "");
TEST_PATH(utf8::find_base_path, "//", "");
TEST_PATH(utf8::find_base_path, "///", "");
TEST_PATH(utf8::find_base_path, "/foo", "");
TEST_PATH(utf8::find_base_path, "//foo", "");
TEST_PATH(utf8::find_base_path, "///foo", "");
TEST_PATH(utf8::find_base_path, "/foo/", "");
TEST_PATH(utf8::find_base_path, "/foo//", "");
TEST_PATH(utf8::find_base_path, "/foo///", "");
TEST_PATH(utf8::find_base_path, "//foo/", "");
TEST_PATH(utf8::find_base_path, "//foo//", "");
TEST_PATH(utf8::find_base_path, "//foo///", "");
TEST_PATH(utf8::find_base_path, "///foo/", "");
TEST_PATH(utf8::find_base_path, "///foo//", "");
TEST_PATH(utf8::find_base_path, "///foo///", "");
TEST_PATH(utf8::find_base_path, "/foo/bar", "/foo");
TEST_PATH(utf8::find_base_path, "/foo/bar/", "/foo");
TEST_PATH(utf8::find_base_path, "/foo/bar//", "/foo");
TEST_PATH(utf8::find_base_path, "/foo/bar///", "/foo");
TEST_PATH(utf8::find_base_path, "/foo/bar/baz/qux", "/foo/bar/baz");
TEST_PATH(utf8::find_base_path, "/foo/bar/baz/qux/", "/foo/bar/baz");
TEST_PATH(utf8::find_base_path, "/foo/bar/baz/qux//", "/foo/bar/baz");
TEST_PATH(utf8::find_base_path, "/foo/bar/baz/qux///", "/foo/bar/baz");
TEST_PATH(utf8::find_base_path, "/ほげ/ぴよ/ふが/ほげら/ほげほげ",
"/ほげ/ぴよ/ふが/ほげら");
TEST_PATH(utf8::find_base_path, "/ほげ/ぴよ/ふが/ほげら/ほげほげ/",
"/ほげ/ぴよ/ふが/ほげら");
TEST_PATH(utf8::find_base_path, "/ほげ/ぴよ/ふが/ほげら/ほげほげ//",
"/ほげ/ぴよ/ふが/ほげら");
TEST_PATH(utf8::find_base_path, "/ほげ/ぴよ/ふが/ほげら/ほげほげ///",
"/ほげ/ぴよ/ふが/ほげら");
TEST_PATH(utf8::find_base_path, "foo", "");
TEST_PATH(utf8::find_base_path, "foo/", "");
TEST_PATH(utf8::find_base_path, "foo//", "");
TEST_PATH(utf8::find_base_path, "foo///", "");
TEST_PATH(utf8::find_base_path, "foo/bar", "foo");
TEST_PATH(utf8::find_base_path, "foo/bar/", "foo");
TEST_PATH(utf8::find_base_path, "foo/bar//", "foo");
TEST_PATH(utf8::find_base_path, "foo/bar///", "foo");
TEST_PATH(utf8::find_base_path, "foo/bar/baz/qux", "foo/bar/baz");
TEST_PATH(utf8::find_base_path, "foo/bar/baz/qux/", "foo/bar/baz");
TEST_PATH(utf8::find_base_path, "foo/bar/baz/qux//", "foo/bar/baz");
TEST_PATH(utf8::find_base_path, "foo/bar/baz/qux///", "foo/bar/baz");
TEST_PATH(utf8::find_base_path, "ほげ/ぴよ/ふが/ほげら/ほげほげ",
"ほげ/ぴよ/ふが/ほげら");
TEST_PATH(utf8::find_base_path, "ほげ/ぴよ/ふが/ほげら/ほげほげ/",
"ほげ/ぴよ/ふが/ほげら");
TEST_PATH(utf8::find_base_path, "ほげ/ぴよ/ふが/ほげら/ほげほげ//",
"ほげ/ぴよ/ふが/ほげら");
TEST_PATH(utf8::find_base_path, "ほげ/ぴよ/ふが/ほげら/ほげほげ///",
"ほげ/ぴよ/ふが/ほげら");
TEST_PATH(utf8::find_base_path, "X:", "");
TEST_PATH(utf8::find_base_path, "X:/", "");
TEST_PATH(utf8::find_base_path, "X://", "");
TEST_PATH(utf8::find_base_path, "X:///", "");
TEST_PATH(utf8::find_base_path, "X:/foo", "X:");
TEST_PATH(utf8::find_base_path, "X:/foo/", "X:");
TEST_PATH(utf8::find_base_path, "X:/foo//", "X:");
TEST_PATH(utf8::find_base_path, "X:/foo///", "X:");
TEST_PATH(utf8::find_base_path, "X:/foo/bar", "X:/foo");
TEST_PATH(utf8::find_base_path, "X:/foo/bar/", "X:/foo");
TEST_PATH(utf8::find_base_path, "X:/foo/bar//", "X:/foo");
TEST_PATH(utf8::find_base_path, "X:/foo/bar///", "X:/foo");
TEST_PATH(utf8::find_base_path, "X:/foo/bar/baz/qux", "X:/foo/bar/baz");
TEST_PATH(utf8::find_base_path, "X:/foo/bar/baz/qux/", "X:/foo/bar/baz");
TEST_PATH(utf8::find_base_path, "X:/foo/bar/baz/qux//", "X:/foo/bar/baz");
TEST_PATH(utf8::find_base_path, "X:/foo/bar/baz/qux///", "X:/foo/bar/baz");
TEST_PATH(utf8::find_base_path, "X:/ほげ/ぴよ/ふが/ほげら/ほげほげ",
"X:/ほげ/ぴよ/ふが/ほげら");
TEST_PATH(utf8::find_base_path, "X:/ほげ/ぴよ/ふが/ほげら/ほげほげ/",
"X:/ほげ/ぴよ/ふが/ほげら");
TEST_PATH(utf8::find_base_path, "X:/ほげ/ぴよ/ふが/ほげら/ほげほげ//",
"X:/ほげ/ぴよ/ふが/ほげら");
TEST_PATH(utf8::find_base_path, "X:/ほげ/ぴよ/ふが/ほげら/ほげほげ///",
"X:/ほげ/ぴよ/ふが/ほげら");
}
// TODO(gibbed): find_base_guest_path
TEST_CASE("utf8::canonicalize_path", "UTF-8 Canonicalize Path") {
TEST_CASE("UTF-8 Canonicalize Path", "[utf8]") {
TEST_PATH(utf8::canonicalize_path, "foo/bar/baz/qux", "foo/bar/baz/qux");
TEST_PATH(utf8::canonicalize_path, "foo/bar/baz/qux/", "foo/bar/baz/qux");
TEST_PATH(utf8::canonicalize_path, "foo/bar/baz/qux//", "foo/bar/baz/qux");
TEST_PATH(utf8::canonicalize_path, "foo/bar/baz/qux///", "foo/bar/baz/qux");
TEST_PATH(utf8::canonicalize_path, "foo/./baz/qux", "foo/baz/qux");
TEST_PATH(utf8::canonicalize_path, "foo/./baz/qux/", "foo/baz/qux");
TEST_PATH(utf8::canonicalize_path, "foo/../baz/qux", "baz/qux");

View File

@ -155,29 +155,36 @@ bool SetTlsValue(TlsHandle handle, uintptr_t value) {
class PosixHighResolutionTimer : public HighResolutionTimer {
public:
explicit PosixHighResolutionTimer(std::function<void()> callback)
: callback_(std::move(callback)), timer_(nullptr) {}
: callback_(std::move(callback)), valid_(false) {}
~PosixHighResolutionTimer() override {
if (timer_) timer_delete(timer_);
if (valid_) timer_delete(timer_);
}
bool Initialize(std::chrono::milliseconds period) {
if (valid_) {
// Double initialization
assert_always();
return false;
}
// Create timer
sigevent sev{};
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = GetSystemSignal(SignalType::kHighResolutionTimer);
sev.sigev_value.sival_ptr = (void*)&callback_;
if (timer_create(CLOCK_REALTIME, &sev, &timer_) == -1) return false;
if (timer_create(CLOCK_MONOTONIC, &sev, &timer_) == -1) return false;
// Start timer
itimerspec its{};
its.it_value = DurationToTimeSpec(period);
its.it_interval = its.it_value;
return timer_settime(timer_, 0, &its, nullptr) != -1;
valid_ = timer_settime(timer_, 0, &its, nullptr) != -1;
return valid_;
}
private:
std::function<void()> callback_;
timer_t timer_;
bool valid_; // all values for timer_t are legal so we need this
};
std::unique_ptr<HighResolutionTimer> HighResolutionTimer::CreateRepeating(
@ -187,7 +194,7 @@ std::unique_ptr<HighResolutionTimer> HighResolutionTimer::CreateRepeating(
if (!timer->Initialize(period)) {
return nullptr;
}
return std::unique_ptr<HighResolutionTimer>(timer.release());
return std::move(timer);
}
class PosixConditionBase {
@ -419,7 +426,7 @@ class PosixCondition<Timer> : public PosixConditionBase {
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = GetSystemSignal(SignalType::kTimer);
sev.sigev_value.sival_ptr = this;
if (timer_create(CLOCK_REALTIME, &sev, &timer_) == -1) return false;
if (timer_create(CLOCK_MONOTONIC, &sev, &timer_) == -1) return false;
}
// Start timer
@ -728,31 +735,44 @@ class PosixCondition<Thread> : public PosixConditionBase {
}
void Terminate(int exit_code) {
bool is_current_thread = pthread_self() == thread_;
{
std::unique_lock<std::mutex> lock(state_mutex_);
if (state_ == State::kFinished) {
if (is_current_thread) {
// This is really bad. Some thread must have called Terminate() on us
// just before we decided to terminate ourselves
assert_always();
for (;;) {
// Wait for pthread_cancel() to actually happen.
}
}
return;
}
state_ = State::kFinished;
}
std::lock_guard<std::mutex> lock(mutex_);
// Sometimes the thread can call terminate twice before stopping
if (thread_ == 0) return;
auto thread = thread_;
exit_code_ = exit_code;
signaled_ = true;
cond_.notify_all();
{
std::lock_guard<std::mutex> lock(mutex_);
exit_code_ = exit_code;
signaled_ = true;
cond_.notify_all();
}
if (is_current_thread) {
pthread_exit(reinterpret_cast<void*>(exit_code));
} else {
#ifdef XE_PLATFORM_ANDROID
if (pthread_kill(thread, GetSystemSignal(SignalType::kThreadTerminate)) !=
0) {
assert_always();
}
if (pthread_kill(thread_,
GetSystemSignal(SignalType::kThreadTerminate)) != 0) {
assert_always();
}
#else
if (pthread_cancel(thread) != 0) {
assert_always();
}
if (pthread_cancel(thread_) != 0) {
assert_always();
}
#endif
}
}
void WaitStarted() const {
@ -778,7 +798,6 @@ class PosixCondition<Thread> : public PosixConditionBase {
inline void post_execution() override {
if (thread_) {
pthread_join(thread_, nullptr);
thread_ = 0;
}
}
pthread_t thread_;
@ -1115,13 +1134,12 @@ Thread* Thread::GetCurrentThread() {
void Thread::Exit(int exit_code) {
if (current_thread_) {
current_thread_->Terminate(exit_code);
// Sometimes the current thread keeps running after being cancelled.
// Prevent other calls from this thread from using current_thread_.
current_thread_ = nullptr;
} else {
// Should only happen with the main thread
pthread_exit(reinterpret_cast<void*>(exit_code));
}
// Function must not return
assert_always();
}
void set_name(const std::string_view name) {

View File

@ -111,30 +111,34 @@ bool SetTlsValue(TlsHandle handle, uintptr_t value) {
class Win32HighResolutionTimer : public HighResolutionTimer {
public:
Win32HighResolutionTimer(std::function<void()> callback)
: callback_(callback) {}
: callback_(std::move(callback)) {}
~Win32HighResolutionTimer() override {
if (handle_) {
if (valid_) {
DeleteTimerQueueTimer(nullptr, handle_, INVALID_HANDLE_VALUE);
handle_ = nullptr;
}
}
bool Initialize(std::chrono::milliseconds period) {
return CreateTimerQueueTimer(
&handle_, nullptr,
[](PVOID param, BOOLEAN timer_or_wait_fired) {
auto timer =
reinterpret_cast<Win32HighResolutionTimer*>(param);
timer->callback_();
},
this, 0, DWORD(period.count()), WT_EXECUTEINTIMERTHREAD)
? true
: false;
if (valid_) {
// Double initialization
assert_always();
return false;
}
valid_ = !!CreateTimerQueueTimer(
&handle_, nullptr,
[](PVOID param, BOOLEAN timer_or_wait_fired) {
auto timer = reinterpret_cast<Win32HighResolutionTimer*>(param);
timer->callback_();
},
this, 0, DWORD(period.count()), WT_EXECUTEINTIMERTHREAD);
return valid_;
}
private:
HANDLE handle_ = nullptr;
std::function<void()> callback_;
HANDLE handle_ = nullptr;
bool valid_ = false; // Documentation does not state which HANDLE is invalid
};
std::unique_ptr<HighResolutionTimer> HighResolutionTimer::CreateRepeating(
@ -143,7 +147,7 @@ std::unique_ptr<HighResolutionTimer> HighResolutionTimer::CreateRepeating(
if (!timer->Initialize(period)) {
return nullptr;
}
return std::unique_ptr<HighResolutionTimer>(timer.release());
return std::move(timer);
}
template <typename T>

View File

@ -582,43 +582,40 @@ std::string find_name_from_path(const std::string_view path,
auto it = end;
--it;
// path is padded with separator
size_t padding = 0;
if (*it == uint32_t(separator)) {
// skip trailing separators at the end of the path
while (*it == uint32_t(separator)) {
if (it == begin) {
// path is all separators, name is empty
return std::string();
}
--it;
padding = 1;
}
// path is just separator
if (it == begin) {
return std::string();
}
// update end so it is before any trailing separators
end = std::next(it);
// search for separator
while (it != begin) {
if (*it == uint32_t(separator)) {
// skip non-separators
while (*it != uint32_t(separator)) {
if (it == begin) {
break;
}
--it;
}
// no separator -- copy entire string (except trailing separator)
if (it == begin) {
return std::string(path.substr(0, path.size() - padding));
// if the iterator is on a separator, advance
if (*it == uint32_t(separator)) {
++it;
}
auto length = byte_length(std::next(it), end);
auto offset = path.length() - length;
return std::string(path.substr(offset, length - padding));
auto offset = byte_length(begin, it);
auto length = byte_length(it, end);
return std::string(path.substr(offset, length));
}
std::string find_base_name_from_path(const std::string_view path,
char32_t separator) {
auto name = find_name_from_path(path, separator);
if (!name.size()) {
if (!name.length()) {
return std::string();
}
@ -653,28 +650,34 @@ std::string find_base_path(const std::string_view path, char32_t separator) {
auto it = end;
--it;
// skip trailing separator
if (*it == uint32_t(separator)) {
// skip trailing separators at the end of the path
while (*it == uint32_t(separator)) {
if (it == begin) {
return std::string();
}
--it;
}
while (it != begin) {
if (*it == uint32_t(separator)) {
break;
// skip non-separators
while (*it != uint32_t(separator)) {
if (it == begin) {
// there are no separators, base path is empty
return std::string();
}
--it;
}
if (it == begin) {
return std::string();
// skip trailing separators at the end of the base path
while (*it == uint32_t(separator)) {
if (it == begin) {
// base path is all separators, base path is empty
return std::string();
}
--it;
}
auto length = byte_length(it, end);
auto offset = path.length() - length;
return std::string(path.substr(0, offset));
auto length = byte_length(begin, std::next(it));
return std::string(path.substr(0, length));
}
std::string canonicalize_path(const std::string_view path, char32_t separator) {

View File

@ -106,18 +106,6 @@ typedef struct alignas(16) vec128_s {
};
};
vec128_s() = default;
vec128_s(const vec128_s& other) {
high = other.high;
low = other.low;
}
vec128_s& operator=(const vec128_s& b) {
high = b.high;
low = b.low;
return *this;
}
bool operator==(const vec128_s& b) const {
return low == b.low && high == b.high;
}

View File

@ -25,6 +25,14 @@ std::shared_ptr<cpptoml::table> ParseFile(
throw cpptoml::parse_exception(xe::path_to_utf8(filename) +
" could not be opened for parsing");
}
// since cpptoml can't parse files with a UTF-8 BOM we need to skip them
char bom[3];
file.read(bom, sizeof(bom));
if (file.fail() || bom[0] != '\xEF' || bom[1] != '\xBB' || bom[2] != '\xBF') {
file.clear();
file.seekg(0);
}
cpptoml::parser p(file);
return p.parse();
}

View File

@ -519,7 +519,7 @@ GuestToHostThunk X64ThunkEmitter::EmitGuestToHostThunk() {
}
// X64Emitter handles actually resolving functions.
extern "C" uint64_t ResolveFunction(void* raw_context, uint32_t target_address);
uint64_t ResolveFunction(void* raw_context, uint64_t target_address);
ResolveFunctionThunk X64ThunkEmitter::EmitResolveFunctionThunk() {
// ebx = target PPC address
@ -548,7 +548,7 @@ ResolveFunctionThunk X64ThunkEmitter::EmitResolveFunctionThunk() {
mov(rcx, rsi); // context
mov(rdx, rbx);
mov(rax, uint64_t(&ResolveFunction));
mov(rax, reinterpret_cast<uint64_t>(&ResolveFunction));
call(rax);
EmitLoadVolatileRegs();

View File

@ -382,15 +382,14 @@ void X64Emitter::UnimplementedInstr(const hir::Instr* i) {
}
// This is used by the X64ThunkEmitter's ResolveFunctionThunk.
extern "C" uint64_t ResolveFunction(void* raw_context,
uint64_t target_address) {
uint64_t ResolveFunction(void* raw_context, uint64_t target_address) {
auto thread_state = *reinterpret_cast<ThreadState**>(raw_context);
// TODO(benvanik): required?
assert_not_zero(target_address);
auto fn =
thread_state->processor()->ResolveFunction((uint32_t)target_address);
auto fn = thread_state->processor()->ResolveFunction(
static_cast<uint32_t>(target_address));
assert_not_null(fn);
auto x64_fn = static_cast<X64Function*>(fn);
uint64_t addr = reinterpret_cast<uint64_t>(x64_fn->machine_code());

View File

@ -105,8 +105,7 @@ struct Op : OpBase {
struct VoidOp : Op<VoidOp, KEY_TYPE_X> {
protected:
template <typename T, KeyType KEY_TYPE>
friend struct Op;
friend struct Op<VoidOp, KEY_TYPE_X>;
template <hir::Opcode OPCODE, typename... Ts>
friend struct I;
void Load(const Instr::Op& op) {}
@ -116,8 +115,7 @@ struct OffsetOp : Op<OffsetOp, KEY_TYPE_O> {
uint64_t value;
protected:
template <typename T, KeyType KEY_TYPE>
friend struct Op;
friend struct Op<OffsetOp, KEY_TYPE_O>;
template <hir::Opcode OPCODE, typename... Ts>
friend struct I;
void Load(const Instr::Op& op) { this->value = op.offset; }
@ -127,8 +125,7 @@ struct SymbolOp : Op<SymbolOp, KEY_TYPE_S> {
Function* value;
protected:
template <typename T, KeyType KEY_TYPE>
friend struct Op;
friend struct Op<SymbolOp, KEY_TYPE_S>;
template <hir::Opcode OPCODE, typename... Ts>
friend struct I;
bool Load(const Instr::Op& op) {
@ -141,8 +138,7 @@ struct LabelOp : Op<LabelOp, KEY_TYPE_L> {
hir::Label* value;
protected:
template <typename T, KeyType KEY_TYPE>
friend struct Op;
friend struct Op<LabelOp, KEY_TYPE_L>;
template <hir::Opcode OPCODE, typename... Ts>
friend struct I;
void Load(const Instr::Op& op) { this->value = op.label; }

View File

@ -2,7 +2,7 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
@ -73,14 +73,14 @@ void DataFlowAnalysisPass::AnalyzeFlow(HIRBuilder* builder,
// Stash for value map. We may want to maintain this during building.
auto arena = builder->arena();
auto value_map = reinterpret_cast<Value**>(
arena->Alloc(sizeof(Value*) * max_value_estimate));
arena->Alloc(sizeof(Value*) * max_value_estimate, alignof(Value)));
// Allocate incoming bitvectors for use by blocks. We don't need outgoing
// because they are only used during the block iteration.
// Mapped by block ordinal.
// TODO(benvanik): cache this list, grow as needed, etc.
auto incoming_bitvectors =
(llvm::BitVector**)arena->Alloc(sizeof(llvm::BitVector*) * block_count);
auto incoming_bitvectors = (llvm::BitVector**)arena->Alloc(
sizeof(llvm::BitVector*) * block_count, alignof(llvm::BitVector));
for (auto n = 0u; n < block_count; n++) {
incoming_bitvectors[n] = new llvm::BitVector(max_value_estimate);
}

View File

@ -2,7 +2,7 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
@ -45,7 +45,7 @@ bool FinalizationPass::Run(HIRBuilder* builder) {
while (label) {
if (!label->name) {
const size_t label_len = 6 + 4;
char* name = reinterpret_cast<char*>(arena->Alloc(label_len + 1));
char* name = reinterpret_cast<char*>(arena->Alloc(label_len + 1, 1));
assert_true(label->id <= 9999);
auto end = fmt::format_to_n(name, label_len, "_label{}", label->id);
name[end.size] = '\0';

View File

@ -39,7 +39,7 @@ enum class ExportCategory : uint8_t {
};
struct ExportTag {
typedef uint32_t type;
using type = uint32_t;
// packed like so:
// ll...... cccccccc ........ ..bihssi

View File

@ -2,7 +2,7 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
@ -739,7 +739,7 @@ void HIRBuilder::Comment(std::string_view value) {
return;
}
auto size = value.size();
auto p = reinterpret_cast<char*>(arena_->Alloc(size + 1));
auto p = reinterpret_cast<char*>(arena_->Alloc(size + 1, 1));
std::memcpy(p, value.data(), size);
p[size] = '\0';
Instr* i = AppendInstr(OPCODE_COMMENT_info, 0);
@ -752,7 +752,7 @@ void HIRBuilder::Comment(const StringBuffer& value) {
return;
}
auto size = value.length();
auto p = reinterpret_cast<char*>(arena_->Alloc(size + 1));
auto p = reinterpret_cast<char*>(arena_->Alloc(size + 1, 1));
std::memcpy(p, value.buffer(), size);
p[size] = '\0';
Instr* i = AppendInstr(OPCODE_COMMENT_info, 0);

View File

@ -2,7 +2,7 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
@ -75,7 +75,7 @@ class HIRBuilder {
template <typename... Args>
void CommentFormat(const std::string_view format, const Args&... args) {
static const uint32_t kMaxCommentSize = 1024;
char* p = reinterpret_cast<char*>(arena_->Alloc(kMaxCommentSize));
char* p = reinterpret_cast<char*>(arena_->Alloc(kMaxCommentSize, 1));
auto result = fmt::format_to_n(p, kMaxCommentSize - 1, format, args...);
p[result.size] = '\0';
size_t rewind = kMaxCommentSize - 1 - result.size;

View File

@ -2,7 +2,7 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
@ -245,65 +245,34 @@ void Value::Convert(TypeName target_type, RoundMode round_mode) {
}
}
template <typename T>
T __inline RoundValue(RoundMode round_mode, T value) {
switch (round_mode) {
case ROUND_TO_ZERO:
return std::trunc(value);
case ROUND_TO_NEAREST:
return std::round(value);
case ROUND_TO_MINUS_INFINITY:
return std::floor(value);
case ROUND_TO_POSITIVE_INFINITY:
return std::ceil(value);
default:
assert_unhandled_case(round_mode);
return value;
}
}
void Value::Round(RoundMode round_mode) {
switch (type) {
case FLOAT32_TYPE:
switch (round_mode) {
case ROUND_TO_ZERO:
constant.f32 = std::trunc(constant.f32);
break;
case ROUND_TO_NEAREST:
constant.f32 = std::round(constant.f32);
return;
case ROUND_TO_MINUS_INFINITY:
constant.f32 = std::floor(constant.f32);
break;
case ROUND_TO_POSITIVE_INFINITY:
constant.f32 = std::ceil(constant.f32);
break;
default:
assert_unhandled_case(round_mode);
return;
}
constant.f32 = RoundValue(round_mode, constant.f32);
return;
case FLOAT64_TYPE:
switch (round_mode) {
case ROUND_TO_ZERO:
constant.f64 = std::trunc(constant.f64);
break;
case ROUND_TO_NEAREST:
constant.f64 = std::round(constant.f64);
return;
case ROUND_TO_MINUS_INFINITY:
constant.f64 = std::floor(constant.f64);
break;
case ROUND_TO_POSITIVE_INFINITY:
constant.f64 = std::ceil(constant.f64);
break;
default:
assert_unhandled_case(round_mode);
return;
}
constant.f64 = RoundValue(round_mode, constant.f64);
return;
case VEC128_TYPE:
for (int i = 0; i < 4; i++) {
switch (round_mode) {
case ROUND_TO_ZERO:
constant.v128.f32[i] = std::trunc(constant.v128.f32[i]);
break;
case ROUND_TO_NEAREST:
constant.v128.f32[i] = std::round(constant.v128.f32[i]);
break;
case ROUND_TO_MINUS_INFINITY:
constant.v128.f32[i] = std::floor(constant.v128.f32[i]);
break;
case ROUND_TO_POSITIVE_INFINITY:
constant.v128.f32[i] = std::ceil(constant.v128.f32[i]);
break;
default:
assert_unhandled_case(round_mode);
return;
}
constant.v128.f32[i] = RoundValue(round_mode, constant.v128.f32[i]);
}
return;
default:

View File

@ -2,7 +2,7 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
@ -426,6 +426,10 @@ int InstrEmit_sc(PPCHIRBuilder& f, const InstrData& i) {
// Game code should only ever use LEV=0.
// LEV=2 is to signify 'call import' from Xenia.
// TODO(gibbed): syscalls!
if (i.SC.LEV == 0) {
f.CallExtern(f.builtins()->syscall_handler);
return 0;
}
if (i.SC.LEV == 2) {
f.CallExtern(f.function());
return 0;
@ -807,5 +811,5 @@ void RegisterEmitCategoryControl() {
}
} // namespace cpu
} // namespace xe
} // namespace cpu
} // namespace xe

View File

@ -10,6 +10,7 @@
#include "xenia/cpu/ppc/ppc_frontend.h"
#include "xenia/base/atomic.h"
#include "xenia/base/logging.h"
#include "xenia/cpu/ppc/ppc_context.h"
#include "xenia/cpu/ppc/ppc_emit.h"
#include "xenia/cpu/ppc/ppc_opcode_info.h"
@ -78,6 +79,17 @@ void LeaveGlobalLock(PPCContext* ppc_context, void* arg0, void* arg1) {
global_mutex->unlock();
}
void SyscallHandler(PPCContext* ppc_context, void* arg0, void* arg1) {
uint64_t syscall_number = ppc_context->r[0];
switch (syscall_number) {
default:
assert_unhandled_case(syscall_number);
XELOGE("Unhandled syscall {}!", syscall_number);
break;
#pragma warning(suppress : 4065)
}
}
bool PPCFrontend::Initialize() {
void* arg0 = reinterpret_cast<void*>(&xe::global_critical_region::mutex());
void* arg1 = reinterpret_cast<void*>(&builtins_.global_lock_count);
@ -87,6 +99,8 @@ bool PPCFrontend::Initialize() {
processor_->DefineBuiltin("EnterGlobalLock", EnterGlobalLock, arg0, arg1);
builtins_.leave_global_lock =
processor_->DefineBuiltin("LeaveGlobalLock", LeaveGlobalLock, arg0, arg1);
builtins_.syscall_handler = processor_->DefineBuiltin(
"SyscallHandler", SyscallHandler, nullptr, nullptr);
return true;
}

View File

@ -33,6 +33,7 @@ struct PPCBuiltins {
Function* check_global_lock;
Function* enter_global_lock;
Function* leave_global_lock;
Function* syscall_handler;
};
class PPCFrontend {

View File

@ -2,7 +2,7 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
@ -104,8 +104,8 @@ bool PPCHIRBuilder::Emit(GuestFunction* function, uint32_t flags) {
// instruction may have a label assigned to it if it hasn't been hit
// yet.
size_t list_size = instr_count_ * sizeof(void*);
instr_offset_list_ = (Instr**)arena_->Alloc(list_size);
label_list_ = (Label**)arena_->Alloc(list_size);
instr_offset_list_ = (Instr**)arena_->Alloc(list_size, alignof(void*));
label_list_ = (Label**)arena_->Alloc(list_size, alignof(void*));
std::memset(instr_offset_list_, 0, list_size);
std::memset(label_list_, 0, list_size);
@ -244,7 +244,7 @@ void PPCHIRBuilder::AnnotateLabel(uint32_t address, Label* label) {
char name_buffer[13];
auto format_result = fmt::format_to_n(name_buffer, 12, "loc_{:08X}", address);
name_buffer[format_result.size] = '\0';
label->name = (char*)arena_->Alloc(sizeof(name_buffer));
label->name = (char*)arena_->Alloc(sizeof(name_buffer), 1);
memcpy(label->name, name_buffer, sizeof(name_buffer));
}

View File

@ -432,12 +432,12 @@ void Processor::LowerIrql(Irql old_value) {
}
bool Processor::Save(ByteStream* stream) {
stream->Write('PROC');
stream->Write(kProcessorSaveSignature);
return true;
}
bool Processor::Restore(ByteStream* stream) {
if (stream->Read<uint32_t>() != 'PROC') {
if (stream->Read<uint32_t>() != kProcessorSaveSignature) {
XELOGE("Processor::Restore - Invalid magic value!");
return false;
}

View File

@ -34,6 +34,8 @@ DECLARE_bool(debug);
namespace xe {
namespace cpu {
constexpr fourcc_t kProcessorSaveSignature = make_fourcc("PROC");
class Breakpoint;
class StackWalker;
class XexModule;

View File

@ -20,7 +20,7 @@
#include "xenia/cpu/processor.h"
#include "xenia/cpu/test_module.h"
#include "third_party/catch/single_include/catch.hpp"
#include "third_party/catch/include/catch.hpp"
#define XENIA_TEST_X64 1

View File

@ -2,7 +2,7 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
@ -249,12 +249,11 @@ int XexModule::ApplyPatch(XexModule* module) {
// Patch base XEX header
uint32_t original_image_size = module->image_size();
uint32_t header_target_size = patch_header->delta_headers_target_offset +
patch_header->delta_headers_source_size;
uint32_t header_target_size = patch_header->size_of_target_headers;
if (!header_target_size) {
header_target_size =
patch_header->size_of_target_headers; // unsure which is more correct..
header_target_size = patch_header->delta_headers_target_offset +
patch_header->delta_headers_source_size;
}
size_t mem_size = module->xex_header_mem_.size();
@ -899,9 +898,9 @@ bool XexModule::Load(const std::string_view name, const std::string_view path,
const void* xex_addr, size_t xex_length) {
auto src_header = reinterpret_cast<const xex2_header*>(xex_addr);
if (src_header->magic == 'XEX1') {
if (src_header->magic == kXEX1Signature) {
xex_format_ = kFormatXex1;
} else if (src_header->magic == 'XEX2') {
} else if (src_header->magic == kXEX2Signature) {
xex_format_ = kFormatXex2;
} else {
return false;
@ -967,6 +966,16 @@ bool XexModule::LoadContinue() {
return false;
}
// Parse any "unsafe" headers into safer variants
xex2_opt_generic_u32* alternate_titleids;
if (GetOptHeader(xex2_header_keys::XEX_HEADER_ALTERNATE_TITLE_IDS,
&alternate_titleids)) {
auto count = alternate_titleids->count();
for (uint32_t i = 0; i < count; i++) {
opt_alternate_title_ids_.push_back(alternate_titleids->values[i]);
}
}
// Scan and find the low/high addresses.
// All code sections are continuous, so this should be easy.
auto heap = memory()->LookupHeap(base_address_);

View File

@ -25,6 +25,10 @@ class KernelState;
namespace xe {
namespace cpu {
constexpr fourcc_t kXEX1Signature = make_fourcc("XEX1");
constexpr fourcc_t kXEX2Signature = make_fourcc("XEX2");
constexpr fourcc_t kElfSignature = make_fourcc(0x7F, 'E', 'L', 'F');
class Runtime;
class XexModule : public xe::cpu::Module {
@ -103,6 +107,10 @@ class XexModule : public xe::cpu::Module {
return retval;
}
std::vector<uint32_t> opt_alternate_title_ids() const {
return opt_alternate_title_ids_;
}
const uint32_t base_address() const { return base_address_; }
const bool is_dev_kit() const { return is_dev_kit_; }
@ -194,6 +202,9 @@ class XexModule : public xe::cpu::Module {
import_libs_; // pre-loaded import libraries for ease of use
std::vector<PESection> pe_sections_;
// XEX_HEADER_ALTERNATE_TITLE_IDS loaded into a safe std::vector
std::vector<uint32_t> opt_alternate_title_ids_;
uint8_t session_key_[0x10];
bool is_dev_kit_ = false;

View File

@ -42,6 +42,7 @@
#include "xenia/ui/imgui_dialog.h"
#include "xenia/vfs/devices/disc_image_device.h"
#include "xenia/vfs/devices/host_path_device.h"
#include "xenia/vfs/devices/null_device.h"
#include "xenia/vfs/devices/stfs_container_device.h"
#include "xenia/vfs/virtual_file_system.h"
@ -279,7 +280,7 @@ X_STATUS Emulator::LaunchXexFile(const std::filesystem::path& path) {
// and then get that symlinked to game:\, so
// -> game:\foo.xex
auto mount_path = "\\Device\\Harddisk0\\Partition0";
auto mount_path = "\\Device\\Harddisk0\\Partition1";
// Register the local directory in the virtual filesystem.
auto parent_path = path.parent_path();
@ -420,7 +421,7 @@ bool Emulator::SaveToFile(const std::filesystem::path& path) {
// Save the emulator state to a file
ByteStream stream(map->data(), map->size());
stream.Write('XSAV');
stream.Write(kEmulatorSaveSignature);
stream.Write(title_id_.has_value());
if (title_id_.has_value()) {
stream.Write(title_id_.value());
@ -454,7 +455,7 @@ bool Emulator::RestoreFromFile(const std::filesystem::path& path) {
auto lock = global_critical_region::AcquireDirect();
ByteStream stream(map->data(), map->size());
if (stream.Read<uint32_t>() != 'XSAV') {
if (stream.Read<uint32_t>() != kEmulatorSaveSignature) {
return false;
}
@ -672,6 +673,25 @@ static std::string format_version(xex2_version version) {
X_STATUS Emulator::CompleteLaunch(const std::filesystem::path& path,
const std::string_view module_path) {
// Setup NullDevices for raw HDD partition accesses
// Cache/STFC code baked into games tries reading/writing to these
// By using a NullDevice that just returns success to all IO requests it
// should allow games to believe cache/raw disk was accessed successfully
// NOTE: this should probably be moved to xenia_main.cc, but right now we need
// to register the \Device\Harddisk0\ NullDevice _after_ the
// \Device\Harddisk0\Partition1 HostPathDevice, otherwise requests to
// Partition1 will go to this. Registering during CompleteLaunch allows us to
// make sure any HostPathDevices are ready beforehand.
// (see comment above cache:\ device registration for more info about why)
auto null_paths = {std::string("\\Partition0"), std::string("\\Cache0"),
std::string("\\Cache1")};
auto null_device =
std::make_unique<vfs::NullDevice>("\\Device\\Harddisk0", null_paths);
if (null_device->Initialize()) {
file_system_->RegisterDevice(std::move(null_device));
}
// Reset state.
title_id_ = std::nullopt;
title_name_ = "";

View File

@ -44,6 +44,8 @@ class Window;
namespace xe {
constexpr fourcc_t kEmulatorSaveSignature = make_fourcc("XSAV");
// The main type that runs the whole emulator.
// This is responsible for initializing and managing all the various subsystems.
class Emulator {

View File

@ -517,6 +517,10 @@ bool CommandProcessor::ExecutePacket(RingBuffer* reader) {
return true;
}
if (packet == 0xCDCDCDCD) {
XELOGW("GPU packet is CDCDCDCD - probably read uninitialized memory!");
}
switch (packet_type) {
case 0x00:
return ExecutePacketType0(reader, packet);
@ -824,8 +828,8 @@ 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->ReadAndSwap<uint32_t>();
assert_true(magic == 'SWAP');
uint32_t magic = reader->ReadAndSwap<fourcc_t>();
assert_true(magic == kSwapSignature);
// TODO(benvanik): only swap frontbuffer ptr.
uint32_t frontbuffer_ptr = reader->ReadAndSwap<uint32_t>();
@ -1145,6 +1149,8 @@ bool CommandProcessor::ExecutePacketType3_EVENT_WRITE_EXT(RingBuffer* reader,
bool CommandProcessor::ExecutePacketType3_EVENT_WRITE_ZPD(RingBuffer* reader,
uint32_t packet,
uint32_t count) {
// Set by D3D as BE but struct ABI is LE
const uint32_t kQueryFinished = xe::byte_swap(0xFFFFFEED);
assert_true(count == 1);
uint32_t initiator = reader->ReadAndSwap<uint32_t>();
// Writeback initiator.
@ -1160,10 +1166,13 @@ bool CommandProcessor::ExecutePacketType3_EVENT_WRITE_ZPD(RingBuffer* reader,
register_file_->values[XE_GPU_REG_RB_SAMPLE_COUNT_ADDR].u32);
// 0xFFFFFEED is written to this two locations by D3D only on D3DISSUE_END
// and used to detect a finished query.
bool isEnd = pSampleCounts->ZPass_A == xe::byte_swap(0xFFFFFEED) &&
pSampleCounts->ZPass_B == xe::byte_swap(0xFFFFFEED);
bool is_end_via_z_pass = pSampleCounts->ZPass_A == kQueryFinished &&
pSampleCounts->ZPass_B == kQueryFinished;
// Older versions of D3D also checks for ZFail (First Gears of War)
bool is_end_via_z_fail = pSampleCounts->ZFail_A == kQueryFinished &&
pSampleCounts->ZFail_B == kQueryFinished;
std::memset(pSampleCounts, 0, sizeof(xe_gpu_depth_sample_counts));
if (isEnd) {
if (is_end_via_z_pass || is_end_via_z_fail) {
pSampleCounts->ZPass_A = fake_sample_count;
pSampleCounts->Total_A = fake_sample_count;
}
@ -1172,40 +1181,77 @@ bool CommandProcessor::ExecutePacketType3_EVENT_WRITE_ZPD(RingBuffer* reader,
return true;
}
bool CommandProcessor::ExecutePacketType3_DRAW_INDX(RingBuffer* reader,
uint32_t packet,
uint32_t count) {
// initiate fetch of index buffer and draw
// if dword0 != 0, this is a conditional draw based on viz query.
bool CommandProcessor::ExecutePacketType3Draw(RingBuffer* reader,
uint32_t packet,
const char* opcode_name,
uint32_t viz_query_condition,
uint32_t count_remaining) {
// if viz_query_condition != 0, this is a conditional draw based on viz query.
// This ID matches the one issued in PM4_VIZ_QUERY
uint32_t dword0 = reader->ReadAndSwap<uint32_t>(); // viz query info
// uint32_t viz_id = dword0 & 0x3F;
// uint32_t viz_id = viz_query_condition & 0x3F;
// when true, render conditionally based on query result
// uint32_t viz_use = dword0 & 0x100;
// uint32_t viz_use = viz_query_condition & 0x100;
assert_not_zero(count_remaining);
if (!count_remaining) {
XELOGE("{}: Packet too small, can't read VGT_DRAW_INITIATOR", opcode_name);
return false;
}
reg::VGT_DRAW_INITIATOR vgt_draw_initiator;
vgt_draw_initiator.value = reader->ReadAndSwap<uint32_t>();
--count_remaining;
WriteRegister(XE_GPU_REG_VGT_DRAW_INITIATOR, vgt_draw_initiator.value);
bool success = true;
// TODO(Triang3l): Remove IndexBufferInfo and replace handling of all this
// with PrimitiveProcessor when the old Vulkan renderer is removed.
bool is_indexed = false;
IndexBufferInfo index_buffer_info;
switch (vgt_draw_initiator.source_select) {
case xenos::SourceSelect::kDMA: {
// Indexed draw.
is_indexed = true;
index_buffer_info.guest_base = reader->ReadAndSwap<uint32_t>();
uint32_t index_size = reader->ReadAndSwap<uint32_t>();
index_buffer_info.endianness =
static_cast<xenos::Endian>(index_size >> 30);
index_size &= 0x00FFFFFF;
// Two separate bounds checks so if there's only one missing register
// value out of two, one uint32_t will be skipped in the command buffer,
// not two.
assert_not_zero(count_remaining);
if (!count_remaining) {
XELOGE("{}: Packet too small, can't read VGT_DMA_BASE", opcode_name);
return false;
}
uint32_t vgt_dma_base = reader->ReadAndSwap<uint32_t>();
--count_remaining;
WriteRegister(XE_GPU_REG_VGT_DMA_BASE, vgt_dma_base);
reg::VGT_DMA_SIZE vgt_dma_size;
assert_not_zero(count_remaining);
if (!count_remaining) {
XELOGE("{}: Packet too small, can't read VGT_DMA_SIZE", opcode_name);
return false;
}
vgt_dma_size.value = reader->ReadAndSwap<uint32_t>();
--count_remaining;
WriteRegister(XE_GPU_REG_VGT_DMA_SIZE, vgt_dma_size.value);
uint32_t index_size_bytes =
vgt_draw_initiator.index_size == xenos::IndexFormat::kInt16
? sizeof(uint16_t)
: sizeof(uint32_t);
// The base address must already be word-aligned according to the R6xx
// documentation, but for safety.
index_buffer_info.guest_base = vgt_dma_base & ~(index_size_bytes - 1);
index_buffer_info.endianness = vgt_dma_size.swap_mode;
index_buffer_info.format = vgt_draw_initiator.index_size;
index_size *=
(vgt_draw_initiator.index_size == xenos::IndexFormat::kInt32) ? 4 : 2;
index_buffer_info.length = index_size;
index_buffer_info.length = vgt_dma_size.num_words * index_size_bytes;
index_buffer_info.count = vgt_draw_initiator.num_indices;
} break;
case xenos::SourceSelect::kImmediate: {
// TODO(Triang3l): VGT_IMMED_DATA.
XELOGE(
"{}: Using immediate vertex indices, which are not supported yet. "
"Report the game to Xenia developers!",
opcode_name, uint32_t(vgt_draw_initiator.source_select));
success = false;
assert_always();
} break;
case xenos::SourceSelect::kAutoIndex: {
@ -1214,71 +1260,65 @@ bool CommandProcessor::ExecutePacketType3_DRAW_INDX(RingBuffer* reader,
index_buffer_info.length = 0;
} break;
default: {
// Invalid source select.
assert_always();
// Invalid source selection.
success = false;
assert_unhandled_case(vgt_draw_initiator.source_select);
} break;
}
auto viz_query = register_file_->Get<reg::PA_SC_VIZ_QUERY>();
if (viz_query.viz_query_ena && viz_query.kill_pix_post_hi_z) {
// TODO(Triang3l): Don't drop the draw call completely if the vertex shader
// has memexport.
// TODO(Triang3l || JoelLinn): Handle this properly in the render backends.
return true;
// Skip to the next command, for example, if there are immediate indexes that
// we don't support yet.
reader->AdvanceRead(count_remaining * sizeof(uint32_t));
if (success) {
auto viz_query = register_file_->Get<reg::PA_SC_VIZ_QUERY>();
if (!(viz_query.viz_query_ena && viz_query.kill_pix_post_hi_z)) {
// TODO(Triang3l): Don't drop the draw call completely if the vertex
// shader has memexport.
// TODO(Triang3l || JoelLinn): Handle this properly in the render
// backends.
success = IssueDraw(
vgt_draw_initiator.prim_type, vgt_draw_initiator.num_indices,
is_indexed ? &index_buffer_info : nullptr,
xenos::IsMajorModeExplicit(vgt_draw_initiator.major_mode,
vgt_draw_initiator.prim_type));
if (!success) {
XELOGE("{}({}, {}, {}): Failed in backend", opcode_name,
vgt_draw_initiator.num_indices,
uint32_t(vgt_draw_initiator.prim_type),
uint32_t(vgt_draw_initiator.source_select));
}
}
}
bool success =
IssueDraw(vgt_draw_initiator.prim_type, vgt_draw_initiator.num_indices,
is_indexed ? &index_buffer_info : nullptr,
xenos::IsMajorModeExplicit(vgt_draw_initiator.major_mode,
vgt_draw_initiator.prim_type));
if (!success) {
XELOGE("PM4_DRAW_INDX({}, {}, {}): Failed in backend",
vgt_draw_initiator.num_indices,
uint32_t(vgt_draw_initiator.prim_type),
uint32_t(vgt_draw_initiator.source_select));
}
return success;
}
return true;
bool CommandProcessor::ExecutePacketType3_DRAW_INDX(RingBuffer* reader,
uint32_t packet,
uint32_t count) {
// "initiate fetch of index buffer and draw"
// Generally used by Xbox 360 Direct3D 9 for kDMA and kAutoIndex sources.
// With a viz query token as the first one.
uint32_t count_remaining = count;
assert_not_zero(count_remaining);
if (!count_remaining) {
XELOGE("PM4_DRAW_INDX: Packet too small, can't read the viz query token");
return false;
}
uint32_t viz_query_condition = reader->ReadAndSwap<uint32_t>();
--count_remaining;
return ExecutePacketType3Draw(reader, packet, "PM4_DRAW_INDX",
viz_query_condition, count_remaining);
}
bool CommandProcessor::ExecutePacketType3_DRAW_INDX_2(RingBuffer* reader,
uint32_t packet,
uint32_t count) {
// draw using supplied indices in packet
reg::VGT_DRAW_INITIATOR vgt_draw_initiator;
vgt_draw_initiator.value = reader->ReadAndSwap<uint32_t>();
WriteRegister(XE_GPU_REG_VGT_DRAW_INITIATOR, vgt_draw_initiator.value);
assert_true(vgt_draw_initiator.source_select ==
xenos::SourceSelect::kAutoIndex);
// Index buffer unused as automatic.
// uint32_t indices_size =
// vgt_draw_initiator.num_indices *
// (vgt_draw_initiator.index_size == xenos::IndexFormat::kInt32 ? 4
// : 2);
// uint32_t index_ptr = reader->ptr();
// TODO(Triang3l): VGT_IMMED_DATA.
reader->AdvanceRead((count - 1) * sizeof(uint32_t));
auto viz_query = register_file_->Get<reg::PA_SC_VIZ_QUERY>();
if (viz_query.viz_query_ena && viz_query.kill_pix_post_hi_z) {
// TODO(Triang3l): Don't drop the draw call completely if the vertex shader
// has memexport.
// TODO(Triang3l || JoelLinn): Handle this properly in the render backends.
return true;
}
bool success = IssueDraw(
vgt_draw_initiator.prim_type, vgt_draw_initiator.num_indices, nullptr,
xenos::IsMajorModeExplicit(vgt_draw_initiator.major_mode,
vgt_draw_initiator.prim_type));
if (!success) {
XELOGE("PM4_DRAW_INDX_IMM({}, {}): Failed in backend",
vgt_draw_initiator.num_indices,
uint32_t(vgt_draw_initiator.prim_type));
}
return true;
// "draw using supplied indices in packet"
// Generally used by Xbox 360 Direct3D 9 for kAutoIndex source.
// No viz query token.
return ExecutePacketType3Draw(reader, packet, "PM4_DRAW_INDX_2", 0, count);
}
bool CommandProcessor::ExecutePacketType3_SET_CONSTANT(RingBuffer* reader,

View File

@ -218,6 +218,10 @@ class CommandProcessor {
uint32_t count);
bool ExecutePacketType3_EVENT_WRITE_ZPD(RingBuffer* reader, uint32_t packet,
uint32_t count);
bool ExecutePacketType3Draw(RingBuffer* reader, uint32_t packet,
const char* opcode_name,
uint32_t viz_query_condition,
uint32_t count_remaining);
bool ExecutePacketType3_DRAW_INDX(RingBuffer* reader, uint32_t packet,
uint32_t count);
bool ExecutePacketType3_DRAW_INDX_2(RingBuffer* reader, uint32_t packet,

View File

@ -81,7 +81,7 @@ void D3D12CommandProcessor::RequestFrameTrace(
void D3D12CommandProcessor::TracePlaybackWroteMemory(uint32_t base_ptr,
uint32_t length) {
shared_memory_->MemoryInvalidationCallback(base_ptr, length, true);
primitive_converter_->MemoryInvalidationCallback(base_ptr, length, true);
primitive_processor_->MemoryInvalidationCallback(base_ptr, length, true);
}
void D3D12CommandProcessor::RestoreEdramSnapshot(const void* snapshot) {
@ -1194,6 +1194,13 @@ bool D3D12CommandProcessor::SetupContext() {
return false;
}
primitive_processor_ = std::make_unique<D3D12PrimitiveProcessor>(
*register_file_, *memory_, trace_writer_, *shared_memory_, *this);
if (!primitive_processor_->Initialize()) {
XELOGE("Failed to initialize the geometric primitive processor");
return false;
}
texture_cache_ = std::make_unique<TextureCache>(
*this, *register_file_, *shared_memory_, bindless_resources_used_,
render_target_cache_->GetResolutionScale());
@ -1210,13 +1217,6 @@ bool D3D12CommandProcessor::SetupContext() {
return false;
}
primitive_converter_ = std::make_unique<PrimitiveConverter>(
*this, *register_file_, *memory_, trace_writer_);
if (!primitive_converter_->Initialize()) {
XELOGE("Failed to initialize the geometric primitive converter");
return false;
}
D3D12_HEAP_FLAGS heap_flag_create_not_zeroed =
provider.GetHeapFlagCreateNotZeroed();
@ -1529,12 +1529,12 @@ void D3D12CommandProcessor::ShutdownContext() {
ui::d3d12::util::ReleaseAndNull(gamma_ramp_upload_);
ui::d3d12::util::ReleaseAndNull(gamma_ramp_texture_);
primitive_converter_.reset();
pipeline_cache_.reset();
texture_cache_.reset();
primitive_processor_.reset();
shared_memory_.reset();
// Shut down binding - bindless descriptors may be owned by subsystems like
@ -1822,13 +1822,13 @@ bool D3D12CommandProcessor::IssueDraw(xenos::PrimitiveType primitive_type,
uint32_t index_count,
IndexBufferInfo* index_buffer_info,
bool major_mode_explicit) {
auto device = GetD3D12Context().GetD3D12Provider().GetDevice();
auto& regs = *register_file_;
#if XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES
SCOPE_profile_cpu_f("gpu");
#endif // XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES
ID3D12Device* device = GetD3D12Context().GetD3D12Provider().GetDevice();
const RegisterFile& regs = *register_file_;
xenos::ModeControl edram_mode = regs.Get<reg::RB_MODECONTROL>().edram_mode;
if (edram_mode == xenos::ModeControl::kCopy) {
// Special copy handling.
@ -1842,7 +1842,7 @@ bool D3D12CommandProcessor::IssueDraw(xenos::PrimitiveType primitive_type,
return true;
}
// Vertex shader.
// Vertex shader analysis.
auto vertex_shader = static_cast<D3D12Shader*>(active_vertex_shader());
if (!vertex_shader) {
// Always need a vertex shader.
@ -1850,16 +1850,9 @@ bool D3D12CommandProcessor::IssueDraw(xenos::PrimitiveType primitive_type,
}
pipeline_cache_->AnalyzeShaderUcode(*vertex_shader);
bool memexport_used_vertex = vertex_shader->is_valid_memexport_used();
DxbcShaderTranslator::Modification vertex_shader_modification;
pipeline_cache_->GetCurrentShaderModification(*vertex_shader,
vertex_shader_modification);
bool tessellated =
vertex_shader_modification.vertex.host_vertex_shader_type !=
Shader::HostVertexShaderType::kVertex;
bool primitive_polygonal =
xenos::IsPrimitivePolygonal(tessellated, primitive_type);
// Pixel shader.
// Pixel shader analysis.
bool primitive_polygonal = draw_util::IsPrimitivePolygonal(regs);
bool is_rasterization_done =
draw_util::IsRasterizationPotentiallyDone(regs, primitive_polygonal);
D3D12Shader* pixel_shader = nullptr;
@ -1884,23 +1877,31 @@ bool D3D12CommandProcessor::IssueDraw(xenos::PrimitiveType primitive_type,
return true;
}
}
bool memexport_used_pixel;
DxbcShaderTranslator::Modification pixel_shader_modification;
if (pixel_shader) {
memexport_used_pixel = pixel_shader->is_valid_memexport_used();
if (!pipeline_cache_->GetCurrentShaderModification(
*pixel_shader, pixel_shader_modification)) {
return false;
}
} else {
memexport_used_pixel = false;
pixel_shader_modification = DxbcShaderTranslator::Modification(0);
}
bool memexport_used_pixel =
pixel_shader && pixel_shader->is_valid_memexport_used();
bool memexport_used = memexport_used_vertex || memexport_used_pixel;
BeginSubmission(true);
// Process primitives.
PrimitiveProcessor::ProcessingResult primitive_processing_result;
if (!primitive_processor_->Process(primitive_processing_result)) {
return false;
}
if (!primitive_processing_result.host_draw_vertex_count) {
// Nothing to draw.
return true;
}
// Shader modifications.
DxbcShaderTranslator::Modification vertex_shader_modification =
pipeline_cache_->GetCurrentVertexShaderModification(
*vertex_shader, primitive_processing_result.host_vertex_shader_type);
DxbcShaderTranslator::Modification pixel_shader_modification =
pixel_shader
? pipeline_cache_->GetCurrentPixelShaderModification(*pixel_shader)
: DxbcShaderTranslator::Modification(0);
// Set up the render targets - this may perform dispatches and draws.
uint32_t pixel_shader_writes_color_targets =
pixel_shader ? pixel_shader->writes_color_targets() : 0;
@ -1909,66 +1910,6 @@ bool D3D12CommandProcessor::IssueDraw(xenos::PrimitiveType primitive_type,
return false;
}
// Set up primitive topology.
bool indexed = index_buffer_info != nullptr && index_buffer_info->guest_base;
xenos::PrimitiveType primitive_type_converted;
D3D_PRIMITIVE_TOPOLOGY primitive_topology;
if (tessellated) {
primitive_type_converted = primitive_type;
switch (primitive_type_converted) {
// TODO(Triang3l): Support all kinds of patches if found in games.
case xenos::PrimitiveType::kTriangleList:
case xenos::PrimitiveType::kTrianglePatch:
primitive_topology = D3D_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST;
break;
case xenos::PrimitiveType::kQuadList:
case xenos::PrimitiveType::kQuadPatch:
primitive_topology = D3D_PRIMITIVE_TOPOLOGY_4_CONTROL_POINT_PATCHLIST;
break;
default:
return false;
}
} else {
primitive_type_converted =
PrimitiveConverter::GetReplacementPrimitiveType(primitive_type);
switch (primitive_type_converted) {
case xenos::PrimitiveType::kPointList:
primitive_topology = D3D_PRIMITIVE_TOPOLOGY_POINTLIST;
break;
case xenos::PrimitiveType::kLineList:
primitive_topology = D3D_PRIMITIVE_TOPOLOGY_LINELIST;
break;
case xenos::PrimitiveType::kLineStrip:
primitive_topology = D3D_PRIMITIVE_TOPOLOGY_LINESTRIP;
break;
case xenos::PrimitiveType::kTriangleList:
case xenos::PrimitiveType::kRectangleList:
primitive_topology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
break;
case xenos::PrimitiveType::kTriangleStrip:
primitive_topology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
break;
case xenos::PrimitiveType::kQuadList:
primitive_topology = D3D_PRIMITIVE_TOPOLOGY_LINELIST_ADJ;
break;
default:
return false;
}
}
SetPrimitiveTopology(primitive_topology);
uint32_t line_loop_closing_index;
if (primitive_type == xenos::PrimitiveType::kLineLoop && !indexed &&
index_count >= 3) {
// Add a vertex to close the loop, and make the vertex shader replace its
// index (before adding the offset) with 0 to fetch the first vertex again.
// For indexed line loops, the primitive converter will add the vertex.
line_loop_closing_index = index_count;
++index_count;
} else {
// Replace index 0 with 0 (do nothing) otherwise.
line_loop_closing_index = 0;
}
// Translate the shaders and create the pipeline if needed.
D3D12Shader::D3D12Translation* vertex_shader_translation =
static_cast<D3D12Shader::D3D12Translation*>(
@ -1987,6 +1928,7 @@ bool D3D12CommandProcessor::IssueDraw(xenos::PrimitiveType primitive_type,
if (host_render_targets_used) {
bound_depth_and_color_render_target_bits =
render_target_cache_->GetLastUpdateBoundRenderTargets(
render_target_cache_->gamma_render_target_as_srgb(),
bound_depth_and_color_render_target_formats);
} else {
bound_depth_and_color_render_target_bits = 0;
@ -1995,9 +1937,7 @@ bool D3D12CommandProcessor::IssueDraw(xenos::PrimitiveType primitive_type,
ID3D12RootSignature* root_signature;
if (!pipeline_cache_->ConfigurePipeline(
vertex_shader_translation, pixel_shader_translation,
primitive_type_converted,
indexed ? index_buffer_info->format : xenos::IndexFormat::kInt16,
bound_depth_and_color_render_target_bits,
primitive_processing_result, bound_depth_and_color_render_target_bits,
bound_depth_and_color_render_target_formats, &pipeline_handle,
&root_signature)) {
return false;
@ -2048,9 +1988,10 @@ bool D3D12CommandProcessor::IssueDraw(xenos::PrimitiveType primitive_type,
// Update system constants before uploading them.
// TODO(Triang3l): With ROV, pass the disabled render target mask for safety.
UpdateSystemConstantValues(
memexport_used, primitive_polygonal, line_loop_closing_index,
indexed ? index_buffer_info->endianness : xenos::Endian::kNone,
viewport_info, used_texture_mask,
memexport_used, primitive_polygonal,
primitive_processing_result.line_loop_closing_index,
primitive_processing_result.host_index_endian, viewport_info,
used_texture_mask,
pixel_shader ? GetCurrentColorMask(pixel_shader->writes_color_targets())
: 0);
@ -2206,112 +2147,139 @@ bool D3D12CommandProcessor::IssueDraw(xenos::PrimitiveType primitive_type,
}
}
// Actually draw.
if (indexed) {
uint32_t index_size =
index_buffer_info->format == xenos::IndexFormat::kInt32
? sizeof(uint32_t)
: sizeof(uint16_t);
assert_false(index_buffer_info->guest_base & (index_size - 1));
uint32_t index_base =
index_buffer_info->guest_base & 0x1FFFFFFF & ~(index_size - 1);
D3D12_INDEX_BUFFER_VIEW index_buffer_view;
index_buffer_view.Format =
index_buffer_info->format == xenos::IndexFormat::kInt32
? DXGI_FORMAT_R32_UINT
: DXGI_FORMAT_R16_UINT;
PrimitiveConverter::ConversionResult conversion_result;
uint32_t converted_index_count;
if (tessellated) {
conversion_result =
PrimitiveConverter::ConversionResult::kConversionNotNeeded;
} else {
conversion_result = primitive_converter_->ConvertPrimitives(
primitive_type, index_buffer_info->guest_base, index_count,
index_buffer_info->format, index_buffer_info->endianness,
index_buffer_view.BufferLocation, converted_index_count);
if (conversion_result == PrimitiveConverter::ConversionResult::kFailed) {
return false;
}
if (conversion_result ==
PrimitiveConverter::ConversionResult::kPrimitiveEmpty) {
return true;
}
}
ID3D12Resource* scratch_index_buffer = nullptr;
if (conversion_result == PrimitiveConverter::ConversionResult::kConverted) {
index_buffer_view.SizeInBytes = converted_index_count * index_size;
index_count = converted_index_count;
} else {
uint32_t index_buffer_size = index_buffer_info->count * index_size;
if (!shared_memory_->RequestRange(index_base, index_buffer_size)) {
// Primitive topology.
D3D_PRIMITIVE_TOPOLOGY primitive_topology;
if (primitive_processing_result.IsTessellated()) {
switch (primitive_processing_result.host_primitive_type) {
// TODO(Triang3l): Support all primitive types.
case xenos::PrimitiveType::kTriangleList:
case xenos::PrimitiveType::kTrianglePatch:
primitive_topology = D3D_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST;
break;
case xenos::PrimitiveType::kQuadList:
case xenos::PrimitiveType::kQuadPatch:
primitive_topology = D3D_PRIMITIVE_TOPOLOGY_4_CONTROL_POINT_PATCHLIST;
break;
default:
XELOGE(
"Failed to request index buffer at 0x{:08X} (size {}) in the "
"shared memory",
index_base, index_buffer_size);
"Host tessellated primitive type {} returned by the primitive "
"processor is not supported by the Direct3D 12 command processor",
uint32_t(primitive_processing_result.host_primitive_type));
assert_unhandled_case(primitive_processing_result.host_primitive_type);
return false;
}
if (memexport_used) {
// If the shared memory is a UAV, it can't be used as an index buffer
// (UAV is a read/write state, index buffer is a read-only state). Need
// to copy the indices to a buffer in the index buffer state.
scratch_index_buffer = RequestScratchGPUBuffer(
index_buffer_size, D3D12_RESOURCE_STATE_COPY_DEST);
if (scratch_index_buffer == nullptr) {
return false;
}
shared_memory_->UseAsCopySource();
SubmitBarriers();
deferred_command_list_.D3DCopyBufferRegion(
scratch_index_buffer, 0, shared_memory_->GetBuffer(), index_base,
index_buffer_size);
PushTransitionBarrier(scratch_index_buffer,
D3D12_RESOURCE_STATE_COPY_DEST,
D3D12_RESOURCE_STATE_INDEX_BUFFER);
index_buffer_view.BufferLocation =
scratch_index_buffer->GetGPUVirtualAddress();
} else {
index_buffer_view.BufferLocation =
shared_memory_->GetGPUAddress() + index_base;
}
index_buffer_view.SizeInBytes = index_buffer_size;
}
} else {
switch (primitive_processing_result.host_primitive_type) {
case xenos::PrimitiveType::kPointList:
primitive_topology = D3D_PRIMITIVE_TOPOLOGY_POINTLIST;
break;
case xenos::PrimitiveType::kLineList:
primitive_topology = D3D_PRIMITIVE_TOPOLOGY_LINELIST;
break;
case xenos::PrimitiveType::kLineStrip:
primitive_topology = D3D_PRIMITIVE_TOPOLOGY_LINESTRIP;
break;
case xenos::PrimitiveType::kTriangleList:
case xenos::PrimitiveType::kRectangleList:
primitive_topology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
break;
case xenos::PrimitiveType::kTriangleStrip:
primitive_topology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
break;
case xenos::PrimitiveType::kQuadList:
primitive_topology = D3D_PRIMITIVE_TOPOLOGY_LINELIST_ADJ;
break;
default:
XELOGE(
"Host primitive type {} returned by the primitive processor is not "
"supported by the Direct3D 12 command processor",
uint32_t(primitive_processing_result.host_primitive_type));
assert_unhandled_case(primitive_processing_result.host_primitive_type);
return false;
}
}
SetPrimitiveTopology(primitive_topology);
// Must not call anything that may change the primitive topology from now on!
// Draw.
if (primitive_processing_result.index_buffer_type ==
PrimitiveProcessor::ProcessedIndexBufferType::kNone) {
if (memexport_used) {
shared_memory_->UseForWriting();
} else {
shared_memory_->UseForReading();
}
SubmitBarriers();
deferred_command_list_.D3DDrawInstanced(
primitive_processing_result.host_draw_vertex_count, 1, 0, 0);
} else {
D3D12_INDEX_BUFFER_VIEW index_buffer_view;
index_buffer_view.SizeInBytes =
primitive_processing_result.host_draw_vertex_count;
if (primitive_processing_result.host_index_format ==
xenos::IndexFormat::kInt16) {
index_buffer_view.SizeInBytes *= sizeof(uint16_t);
index_buffer_view.Format = DXGI_FORMAT_R16_UINT;
} else {
index_buffer_view.SizeInBytes *= sizeof(uint32_t);
index_buffer_view.Format = DXGI_FORMAT_R32_UINT;
}
ID3D12Resource* scratch_index_buffer = nullptr;
switch (primitive_processing_result.index_buffer_type) {
case PrimitiveProcessor::ProcessedIndexBufferType::kGuest: {
if (memexport_used) {
// If the shared memory is a UAV, it can't be used as an index buffer
// (UAV is a read/write state, index buffer is a read-only state).
// Need to copy the indices to a buffer in the index buffer state.
scratch_index_buffer = RequestScratchGPUBuffer(
index_buffer_view.SizeInBytes, D3D12_RESOURCE_STATE_COPY_DEST);
if (scratch_index_buffer == nullptr) {
return false;
}
shared_memory_->UseAsCopySource();
SubmitBarriers();
deferred_command_list_.D3DCopyBufferRegion(
scratch_index_buffer, 0, shared_memory_->GetBuffer(),
primitive_processing_result.guest_index_base,
index_buffer_view.SizeInBytes);
PushTransitionBarrier(scratch_index_buffer,
D3D12_RESOURCE_STATE_COPY_DEST,
D3D12_RESOURCE_STATE_INDEX_BUFFER);
index_buffer_view.BufferLocation =
scratch_index_buffer->GetGPUVirtualAddress();
} else {
index_buffer_view.BufferLocation =
shared_memory_->GetGPUAddress() +
primitive_processing_result.guest_index_base;
}
} break;
case PrimitiveProcessor::ProcessedIndexBufferType::kHostConverted:
index_buffer_view.BufferLocation =
primitive_processor_->GetConvertedIndexBufferGpuAddress(
primitive_processing_result.host_index_buffer_handle);
break;
case PrimitiveProcessor::ProcessedIndexBufferType::kHostBuiltin:
index_buffer_view.BufferLocation =
primitive_processor_->GetBuiltinIndexBufferGpuAddress(
primitive_processing_result.host_index_buffer_handle);
break;
default:
assert_unhandled_case(primitive_processing_result.index_buffer_type);
return false;
}
deferred_command_list_.D3DIASetIndexBuffer(&index_buffer_view);
deferred_command_list_.D3DDrawIndexedInstanced(index_count, 1, 0, 0, 0);
if (memexport_used) {
shared_memory_->UseForWriting();
} else {
shared_memory_->UseForReading();
}
SubmitBarriers();
deferred_command_list_.D3DDrawIndexedInstanced(
primitive_processing_result.host_draw_vertex_count, 1, 0, 0, 0);
if (scratch_index_buffer != nullptr) {
ReleaseScratchGPUBuffer(scratch_index_buffer,
D3D12_RESOURCE_STATE_INDEX_BUFFER);
}
} else {
// Check if need to draw using a conversion index buffer.
uint32_t converted_index_count = 0;
D3D12_GPU_VIRTUAL_ADDRESS conversion_gpu_address =
tessellated ? 0
: primitive_converter_->GetStaticIndexBuffer(
primitive_type, index_count, converted_index_count);
if (memexport_used) {
shared_memory_->UseForWriting();
} else {
shared_memory_->UseForReading();
}
SubmitBarriers();
if (conversion_gpu_address) {
D3D12_INDEX_BUFFER_VIEW index_buffer_view;
index_buffer_view.BufferLocation = conversion_gpu_address;
index_buffer_view.SizeInBytes = converted_index_count * sizeof(uint16_t);
index_buffer_view.Format = DXGI_FORMAT_R16_UINT;
deferred_command_list_.D3DIASetIndexBuffer(&index_buffer_view);
deferred_command_list_.D3DDrawIndexedInstanced(converted_index_count, 1,
0, 0, 0);
} else {
deferred_command_list_.D3DDrawInstanced(index_count, 1, 0, 0);
}
}
if (memexport_used) {
@ -2526,9 +2494,9 @@ void D3D12CommandProcessor::CheckSubmissionFence(uint64_t await_submission) {
shared_memory_->CompletedSubmissionUpdated();
render_target_cache_->CompletedSubmissionUpdated();
primitive_processor_->CompletedSubmissionUpdated();
primitive_converter_->CompletedSubmissionUpdated();
render_target_cache_->CompletedSubmissionUpdated();
}
void D3D12CommandProcessor::BeginSubmission(bool is_guest_command) {
@ -2593,11 +2561,11 @@ void D3D12CommandProcessor::BeginSubmission(bool is_guest_command) {
}
primitive_topology_ = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED;
primitive_processor_->BeginSubmission();
render_target_cache_->BeginSubmission();
texture_cache_->BeginSubmission();
primitive_converter_->BeginSubmission();
}
if (is_opening_frame) {
@ -2645,9 +2613,9 @@ void D3D12CommandProcessor::BeginSubmission(bool is_guest_command) {
}
}
texture_cache_->BeginFrame();
primitive_processor_->BeginFrame();
primitive_converter_->BeginFrame();
texture_cache_->BeginFrame();
}
}
@ -2676,6 +2644,8 @@ bool D3D12CommandProcessor::EndSubmission(bool is_swap) {
if (is_closing_frame) {
texture_cache_->EndFrame();
primitive_processor_->EndFrame();
}
if (submission_open_) {
@ -2762,8 +2732,6 @@ bool D3D12CommandProcessor::EndSubmission(bool is_swap) {
}
constant_buffer_pool_->ClearCache();
primitive_converter_->ClearCache();
pipeline_cache_->ClearCache();
render_target_cache_->ClearCache();
@ -2775,6 +2743,8 @@ bool D3D12CommandProcessor::EndSubmission(bool is_swap) {
}
root_signatures_bindful_.clear();
primitive_processor_->ClearCache();
shared_memory_->ClearCache();
}
}
@ -2897,7 +2867,9 @@ void D3D12CommandProcessor::UpdateSystemConstantValues(
auto rb_surface_info = regs.Get<reg::RB_SURFACE_INFO>();
auto sq_context_misc = regs.Get<reg::SQ_CONTEXT_MISC>();
auto sq_program_cntl = regs.Get<reg::SQ_PROGRAM_CNTL>();
int32_t vgt_indx_offset = int32_t(regs[XE_GPU_REG_VGT_INDX_OFFSET].u32);
uint32_t vgt_indx_offset = regs.Get<reg::VGT_INDX_OFFSET>().indx_offset;
uint32_t vgt_max_vtx_indx = regs.Get<reg::VGT_MAX_VTX_INDX>().max_indx;
uint32_t vgt_min_vtx_indx = regs.Get<reg::VGT_MIN_VTX_INDX>().min_indx;
bool edram_rov_used = render_target_cache_->GetPath() ==
RenderTargetCache::Path::kPixelShaderInterlock;
@ -3050,8 +3022,14 @@ void D3D12CommandProcessor::UpdateSystemConstantValues(
system_constants_.vertex_index_endian = index_endian;
// Vertex index offset.
dirty |= system_constants_.vertex_base_index != vgt_indx_offset;
system_constants_.vertex_base_index = vgt_indx_offset;
dirty |= system_constants_.vertex_index_offset != vgt_indx_offset;
system_constants_.vertex_index_offset = vgt_indx_offset;
// Vertex index range.
dirty |= system_constants_.vertex_index_min != vgt_min_vtx_indx;
dirty |= system_constants_.vertex_index_max != vgt_max_vtx_indx;
system_constants_.vertex_index_min = vgt_min_vtx_indx;
system_constants_.vertex_index_max = vgt_max_vtx_indx;
// User clip planes (UCP_ENA_#), when not CLIP_DISABLE.
if (!pa_cl_clip_cntl.clip_disable) {
@ -3082,14 +3060,14 @@ void D3D12CommandProcessor::UpdateSystemConstantValues(
float point_size_y = float(pa_su_point_size.height) * 0.125f;
float point_size_min = float(pa_su_point_minmax.min_size) * 0.125f;
float point_size_max = float(pa_su_point_minmax.max_size) * 0.125f;
dirty |= system_constants_.point_size[0] != point_size_x;
dirty |= system_constants_.point_size[1] != point_size_y;
dirty |= system_constants_.point_size_min_max[0] != point_size_min;
dirty |= system_constants_.point_size_min_max[1] != point_size_max;
system_constants_.point_size[0] = point_size_x;
system_constants_.point_size[1] = point_size_y;
system_constants_.point_size_min_max[0] = point_size_min;
system_constants_.point_size_min_max[1] = point_size_max;
dirty |= system_constants_.point_size_x != point_size_x;
dirty |= system_constants_.point_size_y != point_size_y;
dirty |= system_constants_.point_size_min != point_size_min;
dirty |= system_constants_.point_size_max != point_size_max;
system_constants_.point_size_x = point_size_x;
system_constants_.point_size_y = point_size_y;
system_constants_.point_size_min = point_size_min;
system_constants_.point_size_max = point_size_max;
float point_screen_to_ndc_x =
(/* 0.5f * 2.0f * */ float(resolution_scale)) /
std::max(viewport_info.xy_extent[0], uint32_t(1));
@ -3236,12 +3214,6 @@ void D3D12CommandProcessor::UpdateSystemConstantValues(
dirty |= system_constants_.edram_depth_base_dwords != depth_base_dwords;
system_constants_.edram_depth_base_dwords = depth_base_dwords;
float depth_range_scale = viewport_info.z_max - viewport_info.z_min;
dirty |= system_constants_.edram_depth_range_scale != depth_range_scale;
system_constants_.edram_depth_range_scale = depth_range_scale;
dirty |= system_constants_.edram_depth_range_offset != viewport_info.z_min;
system_constants_.edram_depth_range_offset = viewport_info.z_min;
// For non-polygons, front polygon offset is used, and it's enabled if
// POLY_OFFSET_PARA_ENABLED is set, for polygons, separate front and back
// are used.
@ -3270,10 +3242,10 @@ void D3D12CommandProcessor::UpdateSystemConstantValues(
poly_offset_back_offset = poly_offset_front_offset;
}
}
// "slope computed in subpixels (1/12 or 1/16)" - R5xx Acceleration. Also:
// https://github.com/mesa3d/mesa/blob/54ad9b444c8e73da498211870e785239ad3ff1aa/src/gallium/drivers/radeonsi/si_state.c#L943
poly_offset_front_scale *= (1.0f / 16.0f) * resolution_scale;
poly_offset_back_scale *= (1.0f / 16.0f) * resolution_scale;
float poly_offset_scale_factor =
xenos::kPolygonOffsetScaleSubpixelUnit * resolution_scale;
poly_offset_front_scale *= poly_offset_scale_factor;
poly_offset_back_scale *= poly_offset_scale_factor;
dirty |= system_constants_.edram_poly_offset_front_scale !=
poly_offset_front_scale;
system_constants_.edram_poly_offset_front_scale = poly_offset_front_scale;

View File

@ -20,11 +20,11 @@
#include "xenia/base/assert.h"
#include "xenia/gpu/command_processor.h"
#include "xenia/gpu/d3d12/d3d12_graphics_system.h"
#include "xenia/gpu/d3d12/d3d12_primitive_processor.h"
#include "xenia/gpu/d3d12/d3d12_render_target_cache.h"
#include "xenia/gpu/d3d12/d3d12_shared_memory.h"
#include "xenia/gpu/d3d12/deferred_command_list.h"
#include "xenia/gpu/d3d12/pipeline_cache.h"
#include "xenia/gpu/d3d12/primitive_converter.h"
#include "xenia/gpu/d3d12/texture_cache.h"
#include "xenia/gpu/draw_util.h"
#include "xenia/gpu/dxbc_shader.h"
@ -490,12 +490,12 @@ class D3D12CommandProcessor : public CommandProcessor {
std::unique_ptr<D3D12SharedMemory> shared_memory_;
std::unique_ptr<D3D12PrimitiveProcessor> primitive_processor_;
std::unique_ptr<PipelineCache> pipeline_cache_;
std::unique_ptr<TextureCache> texture_cache_;
std::unique_ptr<PrimitiveConverter> primitive_converter_;
// Mip 0 contains the normal gamma ramp (256 entries), mip 1 contains the PWL
// ramp (128 entries). DXGI_FORMAT_R10G10B10A2_UNORM 1D.
ID3D12Resource* gamma_ramp_texture_ = nullptr;

View File

@ -9,6 +9,8 @@
#include "xenia/gpu/d3d12/d3d12_graphics_system.h"
#include <algorithm>
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
#include "xenia/gpu/d3d12/d3d12_command_processor.h"
@ -20,10 +22,12 @@ namespace xe {
namespace gpu {
namespace d3d12 {
// Generated with `xb buildhlsl`.
#include "xenia/gpu/d3d12/shaders/dxbc/fullscreen_tc_vs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/stretch_gamma_ps.h"
#include "xenia/gpu/d3d12/shaders/dxbc/stretch_ps.h"
// Generated with `xb buildshaders`.
namespace shaders {
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/fullscreen_tc_vs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/stretch_gamma_ps.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/stretch_ps.h"
} // namespace shaders
D3D12GraphicsSystem::D3D12GraphicsSystem() {}
@ -138,10 +142,10 @@ X_STATUS D3D12GraphicsSystem::Setup(cpu::Processor* processor,
// Create the stretch pipelines.
D3D12_GRAPHICS_PIPELINE_STATE_DESC stretch_pipeline_desc = {};
stretch_pipeline_desc.pRootSignature = stretch_root_signature_;
stretch_pipeline_desc.VS.pShaderBytecode = fullscreen_tc_vs;
stretch_pipeline_desc.VS.BytecodeLength = sizeof(fullscreen_tc_vs);
stretch_pipeline_desc.PS.pShaderBytecode = stretch_ps;
stretch_pipeline_desc.PS.BytecodeLength = sizeof(stretch_ps);
stretch_pipeline_desc.VS.pShaderBytecode = shaders::fullscreen_tc_vs;
stretch_pipeline_desc.VS.BytecodeLength = sizeof(shaders::fullscreen_tc_vs);
stretch_pipeline_desc.PS.pShaderBytecode = shaders::stretch_ps;
stretch_pipeline_desc.PS.BytecodeLength = sizeof(shaders::stretch_ps);
// The shader will set alpha to 1, don't use output-merger to preserve it.
stretch_pipeline_desc.BlendState.RenderTarget[0].RenderTargetWriteMask =
D3D12_COLOR_WRITE_ENABLE_ALL;
@ -165,8 +169,8 @@ X_STATUS D3D12GraphicsSystem::Setup(cpu::Processor* processor,
return X_STATUS_UNSUCCESSFUL;
}
stretch_pipeline_desc.pRootSignature = stretch_gamma_root_signature_;
stretch_pipeline_desc.PS.pShaderBytecode = stretch_gamma_ps;
stretch_pipeline_desc.PS.BytecodeLength = sizeof(stretch_gamma_ps);
stretch_pipeline_desc.PS.pShaderBytecode = shaders::stretch_gamma_ps;
stretch_pipeline_desc.PS.BytecodeLength = sizeof(shaders::stretch_gamma_ps);
if (FAILED(device->CreateGraphicsPipelineState(
&stretch_pipeline_desc, IID_PPV_ARGS(&stretch_gamma_pipeline_)))) {
XELOGE(

View File

@ -0,0 +1,174 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/gpu/d3d12/d3d12_primitive_processor.h"
#include <algorithm>
#include <cstdint>
#include <memory>
#include <utility>
#include "xenia/base/assert.h"
#include "xenia/base/logging.h"
#include "xenia/gpu/d3d12/d3d12_command_processor.h"
#include "xenia/gpu/d3d12/deferred_command_list.h"
#include "xenia/ui/d3d12/d3d12_provider.h"
#include "xenia/ui/d3d12/d3d12_util.h"
namespace xe {
namespace gpu {
namespace d3d12 {
D3D12PrimitiveProcessor::~D3D12PrimitiveProcessor() { Shutdown(true); }
bool D3D12PrimitiveProcessor::Initialize() {
if (!InitializeCommon(true, false, false, true)) {
Shutdown();
return false;
}
frame_index_buffer_pool_ = std::make_unique<ui::d3d12::D3D12UploadBufferPool>(
command_processor_.GetD3D12Context().GetD3D12Provider(),
std::max(size_t(kMinRequiredConvertedIndexBufferSize),
ui::GraphicsUploadBufferPool::kDefaultPageSize));
return true;
}
void D3D12PrimitiveProcessor::Shutdown(bool from_destructor) {
frame_index_buffers_.clear();
frame_index_buffer_pool_.reset();
builtin_index_buffer_upload_.Reset();
builtin_index_buffer_gpu_address_ = 0;
builtin_index_buffer_.Reset();
if (!from_destructor) {
ShutdownCommon();
}
}
void D3D12PrimitiveProcessor::CompletedSubmissionUpdated() {
if (builtin_index_buffer_upload_ &&
command_processor_.GetCompletedSubmission() >=
builtin_index_buffer_upload_submission_) {
builtin_index_buffer_upload_.Reset();
}
}
void D3D12PrimitiveProcessor::BeginSubmission() {
if (builtin_index_buffer_upload_ &&
builtin_index_buffer_upload_submission_ == UINT64_MAX) {
// No need to submit deferred barriers - builtin_index_buffer_ has never
// been used yet, so it's in the initial state, and
// builtin_index_buffer_upload_ is in an upload heap, so it's GENERIC_READ.
command_processor_.GetDeferredCommandList().D3DCopyResource(
builtin_index_buffer_.Get(), builtin_index_buffer_upload_.Get());
command_processor_.PushTransitionBarrier(builtin_index_buffer_.Get(),
D3D12_RESOURCE_STATE_COPY_DEST,
D3D12_RESOURCE_STATE_INDEX_BUFFER);
builtin_index_buffer_upload_submission_ =
command_processor_.GetCurrentSubmission();
}
}
void D3D12PrimitiveProcessor::BeginFrame() {
frame_index_buffer_pool_->Reclaim(command_processor_.GetCompletedFrame());
}
void D3D12PrimitiveProcessor::EndFrame() {
ClearPerFrameCache();
frame_index_buffers_.clear();
}
bool D3D12PrimitiveProcessor::InitializeBuiltin16BitIndexBuffer(
uint32_t index_count, std::function<void(uint16_t*)> fill_callback) {
assert_not_zero(index_count);
assert_null(builtin_index_buffer_);
assert_null(builtin_index_buffer_upload_);
const ui::d3d12::D3D12Provider& provider =
command_processor_.GetD3D12Context().GetD3D12Provider();
ID3D12Device* device = provider.GetDevice();
D3D12_RESOURCE_DESC resource_desc;
ui::d3d12::util::FillBufferResourceDesc(
resource_desc, UINT64(sizeof(uint16_t) * index_count),
D3D12_RESOURCE_FLAG_NONE);
Microsoft::WRL::ComPtr<ID3D12Resource> draw_resource;
if (FAILED(device->CreateCommittedResource(
&ui::d3d12::util::kHeapPropertiesDefault,
provider.GetHeapFlagCreateNotZeroed(), &resource_desc,
D3D12_RESOURCE_STATE_COPY_DEST, nullptr,
IID_PPV_ARGS(&draw_resource)))) {
XELOGE(
"D3D12 primitive processor: Failed to create the built-in index "
"buffer GPU resource with {} 16-bit indices",
index_count);
return false;
}
Microsoft::WRL::ComPtr<ID3D12Resource> upload_resource;
if (FAILED(device->CreateCommittedResource(
&ui::d3d12::util::kHeapPropertiesUpload,
provider.GetHeapFlagCreateNotZeroed(), &resource_desc,
D3D12_RESOURCE_STATE_GENERIC_READ, nullptr,
IID_PPV_ARGS(&upload_resource)))) {
XELOGE(
"D3D12 primitive processor: Failed to create the built-in index "
"buffer upload resource with {} 16-bit indices",
index_count);
return false;
}
D3D12_RANGE upload_read_range = {};
void* mapping;
if (FAILED(upload_resource->Map(0, &upload_read_range, &mapping))) {
XELOGE(
"D3D12 primitive processor: Failed to map the built-in index buffer "
"upload resource with {} 16-bit indices",
index_count);
return false;
}
fill_callback(reinterpret_cast<uint16_t*>(mapping));
upload_resource->Unmap(0, nullptr);
// Successfully created the buffer and wrote the data to upload.
builtin_index_buffer_ = std::move(draw_resource);
builtin_index_buffer_gpu_address_ =
builtin_index_buffer_->GetGPUVirtualAddress();
builtin_index_buffer_upload_ = std::move(upload_resource);
// Schedule uploading in the first submission.
builtin_index_buffer_upload_submission_ = UINT64_MAX;
return true;
}
void* D3D12PrimitiveProcessor::RequestHostConvertedIndexBufferForCurrentFrame(
xenos::IndexFormat format, uint32_t index_count, bool coalign_for_simd,
uint32_t coalignment_original_address, size_t& backend_handle_out) {
size_t index_size = format == xenos::IndexFormat::kInt16 ? sizeof(uint16_t)
: sizeof(uint32_t);
D3D12_GPU_VIRTUAL_ADDRESS gpu_address;
uint8_t* mapping = frame_index_buffer_pool_->Request(
command_processor_.GetCurrentFrame(),
index_size * index_count +
(coalign_for_simd ? XE_GPU_PRIMITIVE_PROCESSOR_SIMD_SIZE : 0),
index_size, nullptr, nullptr, &gpu_address);
if (!mapping) {
return false;
}
if (coalign_for_simd) {
ptrdiff_t coalignment_offset =
GetSimdCoalignmentOffset(mapping, coalignment_original_address);
mapping += coalignment_offset;
gpu_address = D3D12_GPU_VIRTUAL_ADDRESS(gpu_address + coalignment_offset);
}
backend_handle_out = frame_index_buffers_.size();
frame_index_buffers_.push_back(gpu_address);
return mapping;
}
} // namespace d3d12
} // namespace gpu
} // namespace xe

View File

@ -0,0 +1,90 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_GPU_D3D12_D3D12_PRIMITIVE_PROCESSOR_H_
#define XENIA_GPU_D3D12_D3D12_PRIMITIVE_PROCESSOR_H_
#include <cstdint>
#include <deque>
#include <memory>
#include "xenia/base/assert.h"
#include "xenia/gpu/primitive_processor.h"
#include "xenia/ui/d3d12/d3d12_api.h"
#include "xenia/ui/d3d12/d3d12_upload_buffer_pool.h"
namespace xe {
namespace gpu {
namespace d3d12 {
class D3D12CommandProcessor;
class D3D12PrimitiveProcessor final : public PrimitiveProcessor {
public:
D3D12PrimitiveProcessor(const RegisterFile& register_file, Memory& memory,
TraceWriter& trace_writer,
SharedMemory& shared_memory,
D3D12CommandProcessor& command_processor)
: PrimitiveProcessor(register_file, memory, trace_writer, shared_memory),
command_processor_(command_processor) {}
~D3D12PrimitiveProcessor();
bool Initialize();
void Shutdown(bool from_destructor = false);
void ClearCache() { frame_index_buffer_pool_->ClearCache(); }
void CompletedSubmissionUpdated();
void BeginSubmission();
void BeginFrame();
void EndFrame();
D3D12_GPU_VIRTUAL_ADDRESS GetBuiltinIndexBufferGpuAddress(
size_t handle) const {
assert_not_null(builtin_index_buffer_);
return D3D12_GPU_VIRTUAL_ADDRESS(builtin_index_buffer_gpu_address_ +
GetBuiltinIndexBufferOffsetBytes(handle));
}
D3D12_GPU_VIRTUAL_ADDRESS GetConvertedIndexBufferGpuAddress(
size_t handle) const {
return frame_index_buffers_[handle];
}
protected:
bool InitializeBuiltin16BitIndexBuffer(
uint32_t index_count,
std::function<void(uint16_t*)> fill_callback) override;
void* RequestHostConvertedIndexBufferForCurrentFrame(
xenos::IndexFormat format, uint32_t index_count, bool coalign_for_simd,
uint32_t coalignment_original_address,
size_t& backend_handle_out) override;
private:
D3D12CommandProcessor& command_processor_;
Microsoft::WRL::ComPtr<ID3D12Resource> builtin_index_buffer_;
D3D12_GPU_VIRTUAL_ADDRESS builtin_index_buffer_gpu_address_ = 0;
// Temporary buffer copied in the beginning of the first submission for
// uploading to builtin_index_buffer_, destroyed when the submission when it
// was uploaded is completed.
Microsoft::WRL::ComPtr<ID3D12Resource> builtin_index_buffer_upload_;
// UINT64_MAX means not uploaded yet and needs uploading in the first
// submission (if the upload buffer exists at all).
uint64_t builtin_index_buffer_upload_submission_ = UINT64_MAX;
std::unique_ptr<ui::d3d12::D3D12UploadBufferPool> frame_index_buffer_pool_;
// Indexed by the backend handles.
std::deque<D3D12_GPU_VIRTUAL_ADDRESS> frame_index_buffers_;
};
} // namespace d3d12
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_D3D12_D3D12_PRIMITIVE_PROCESSOR_H_

View File

@ -74,91 +74,108 @@ namespace xe {
namespace gpu {
namespace d3d12 {
// Generated with `xb buildhlsl`.
#include "xenia/gpu/d3d12/shaders/dxbc/clear_uint2_ps.h"
#include "xenia/gpu/d3d12/shaders/dxbc/fullscreen_vs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/host_depth_store_1xmsaa_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/host_depth_store_2xmsaa_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/host_depth_store_4xmsaa_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/passthrough_position_xy_vs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/resolve_clear_32bpp_2xres_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/resolve_clear_32bpp_3xres_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/resolve_clear_32bpp_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/resolve_clear_64bpp_2xres_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/resolve_clear_64bpp_3xres_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/resolve_clear_64bpp_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/resolve_fast_32bpp_1x2xmsaa_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/resolve_fast_32bpp_2xres_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/resolve_fast_32bpp_3xres_1x2xmsaa_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/resolve_fast_32bpp_3xres_4xmsaa_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/resolve_fast_32bpp_4xmsaa_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/resolve_fast_64bpp_1x2xmsaa_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/resolve_fast_64bpp_2xres_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/resolve_fast_64bpp_3xres_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/resolve_fast_64bpp_4xmsaa_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/resolve_full_128bpp_2xres_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/resolve_full_128bpp_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/resolve_full_128bpp_from_32bpp_3xres_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/resolve_full_128bpp_from_64bpp_3xres_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/resolve_full_16bpp_2xres_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/resolve_full_16bpp_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/resolve_full_16bpp_from_32bpp_3xres_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/resolve_full_16bpp_from_64bpp_3xres_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/resolve_full_32bpp_2xres_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/resolve_full_32bpp_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/resolve_full_32bpp_from_32bpp_3xres_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/resolve_full_32bpp_from_64bpp_3xres_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/resolve_full_64bpp_2xres_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/resolve_full_64bpp_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/resolve_full_64bpp_from_32bpp_3xres_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/resolve_full_64bpp_from_64bpp_3xres_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/resolve_full_8bpp_2xres_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/resolve_full_8bpp_3xres_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/resolve_full_8bpp_cs.h"
// Generated with `xb buildshaders`.
namespace shaders {
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/clear_uint2_ps.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/fullscreen_vs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/host_depth_store_1xmsaa_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/host_depth_store_2xmsaa_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/host_depth_store_4xmsaa_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/passthrough_position_xy_vs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_clear_32bpp_2xres_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_clear_32bpp_3xres_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_clear_32bpp_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_clear_64bpp_2xres_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_clear_64bpp_3xres_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_clear_64bpp_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_32bpp_1x2xmsaa_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_32bpp_2xres_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_32bpp_3xres_1x2xmsaa_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_32bpp_3xres_4xmsaa_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_32bpp_4xmsaa_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_64bpp_1x2xmsaa_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_64bpp_2xres_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_64bpp_3xres_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_64bpp_4xmsaa_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_128bpp_2xres_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_128bpp_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_128bpp_from_32bpp_3xres_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_128bpp_from_64bpp_3xres_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_16bpp_2xres_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_16bpp_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_16bpp_from_32bpp_3xres_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_16bpp_from_64bpp_3xres_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_32bpp_2xres_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_32bpp_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_32bpp_from_32bpp_3xres_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_32bpp_from_64bpp_3xres_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_64bpp_2xres_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_64bpp_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_64bpp_from_32bpp_3xres_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_64bpp_from_64bpp_3xres_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_8bpp_2xres_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_8bpp_3xres_cs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_8bpp_cs.h"
} // namespace shaders
const std::pair<const uint8_t*, size_t>
const std::pair<const void*, size_t>
D3D12RenderTargetCache::kResolveCopyShaders[size_t(
draw_util::ResolveCopyShaderIndex::kCount)] = {
{resolve_fast_32bpp_1x2xmsaa_cs,
sizeof(resolve_fast_32bpp_1x2xmsaa_cs)},
{resolve_fast_32bpp_4xmsaa_cs, sizeof(resolve_fast_32bpp_4xmsaa_cs)},
{resolve_fast_32bpp_2xres_cs, sizeof(resolve_fast_32bpp_2xres_cs)},
{resolve_fast_32bpp_3xres_1x2xmsaa_cs,
sizeof(resolve_fast_32bpp_3xres_1x2xmsaa_cs)},
{resolve_fast_32bpp_3xres_4xmsaa_cs,
sizeof(resolve_fast_32bpp_3xres_4xmsaa_cs)},
{resolve_fast_64bpp_1x2xmsaa_cs,
sizeof(resolve_fast_64bpp_1x2xmsaa_cs)},
{resolve_fast_64bpp_4xmsaa_cs, sizeof(resolve_fast_64bpp_4xmsaa_cs)},
{resolve_fast_64bpp_2xres_cs, sizeof(resolve_fast_64bpp_2xres_cs)},
{resolve_fast_64bpp_3xres_cs, sizeof(resolve_fast_64bpp_3xres_cs)},
{resolve_full_8bpp_cs, sizeof(resolve_full_8bpp_cs)},
{resolve_full_8bpp_2xres_cs, sizeof(resolve_full_8bpp_2xres_cs)},
{resolve_full_8bpp_3xres_cs, sizeof(resolve_full_8bpp_3xres_cs)},
{resolve_full_16bpp_cs, sizeof(resolve_full_16bpp_cs)},
{resolve_full_16bpp_2xres_cs, sizeof(resolve_full_16bpp_2xres_cs)},
{resolve_full_16bpp_from_32bpp_3xres_cs,
sizeof(resolve_full_16bpp_from_32bpp_3xres_cs)},
{resolve_full_16bpp_from_64bpp_3xres_cs,
sizeof(resolve_full_16bpp_from_64bpp_3xres_cs)},
{resolve_full_32bpp_cs, sizeof(resolve_full_32bpp_cs)},
{resolve_full_32bpp_2xres_cs, sizeof(resolve_full_32bpp_2xres_cs)},
{resolve_full_32bpp_from_32bpp_3xres_cs,
sizeof(resolve_full_32bpp_from_32bpp_3xres_cs)},
{resolve_full_32bpp_from_64bpp_3xres_cs,
sizeof(resolve_full_32bpp_from_64bpp_3xres_cs)},
{resolve_full_64bpp_cs, sizeof(resolve_full_64bpp_cs)},
{resolve_full_64bpp_2xres_cs, sizeof(resolve_full_64bpp_2xres_cs)},
{resolve_full_64bpp_from_32bpp_3xres_cs,
sizeof(resolve_full_64bpp_from_32bpp_3xres_cs)},
{resolve_full_64bpp_from_64bpp_3xres_cs,
sizeof(resolve_full_64bpp_from_64bpp_3xres_cs)},
{resolve_full_128bpp_cs, sizeof(resolve_full_128bpp_cs)},
{resolve_full_128bpp_2xres_cs, sizeof(resolve_full_128bpp_2xres_cs)},
{resolve_full_128bpp_from_32bpp_3xres_cs,
sizeof(resolve_full_128bpp_from_32bpp_3xres_cs)},
{resolve_full_128bpp_from_64bpp_3xres_cs,
sizeof(resolve_full_128bpp_from_64bpp_3xres_cs)},
{shaders::resolve_fast_32bpp_1x2xmsaa_cs,
sizeof(shaders::resolve_fast_32bpp_1x2xmsaa_cs)},
{shaders::resolve_fast_32bpp_4xmsaa_cs,
sizeof(shaders::resolve_fast_32bpp_4xmsaa_cs)},
{shaders::resolve_fast_32bpp_2xres_cs,
sizeof(shaders::resolve_fast_32bpp_2xres_cs)},
{shaders::resolve_fast_32bpp_3xres_1x2xmsaa_cs,
sizeof(shaders::resolve_fast_32bpp_3xres_1x2xmsaa_cs)},
{shaders::resolve_fast_32bpp_3xres_4xmsaa_cs,
sizeof(shaders::resolve_fast_32bpp_3xres_4xmsaa_cs)},
{shaders::resolve_fast_64bpp_1x2xmsaa_cs,
sizeof(shaders::resolve_fast_64bpp_1x2xmsaa_cs)},
{shaders::resolve_fast_64bpp_4xmsaa_cs,
sizeof(shaders::resolve_fast_64bpp_4xmsaa_cs)},
{shaders::resolve_fast_64bpp_2xres_cs,
sizeof(shaders::resolve_fast_64bpp_2xres_cs)},
{shaders::resolve_fast_64bpp_3xres_cs,
sizeof(shaders::resolve_fast_64bpp_3xres_cs)},
{shaders::resolve_full_8bpp_cs, sizeof(shaders::resolve_full_8bpp_cs)},
{shaders::resolve_full_8bpp_2xres_cs,
sizeof(shaders::resolve_full_8bpp_2xres_cs)},
{shaders::resolve_full_8bpp_3xres_cs,
sizeof(shaders::resolve_full_8bpp_3xres_cs)},
{shaders::resolve_full_16bpp_cs,
sizeof(shaders::resolve_full_16bpp_cs)},
{shaders::resolve_full_16bpp_2xres_cs,
sizeof(shaders::resolve_full_16bpp_2xres_cs)},
{shaders::resolve_full_16bpp_from_32bpp_3xres_cs,
sizeof(shaders::resolve_full_16bpp_from_32bpp_3xres_cs)},
{shaders::resolve_full_16bpp_from_64bpp_3xres_cs,
sizeof(shaders::resolve_full_16bpp_from_64bpp_3xres_cs)},
{shaders::resolve_full_32bpp_cs,
sizeof(shaders::resolve_full_32bpp_cs)},
{shaders::resolve_full_32bpp_2xres_cs,
sizeof(shaders::resolve_full_32bpp_2xres_cs)},
{shaders::resolve_full_32bpp_from_32bpp_3xres_cs,
sizeof(shaders::resolve_full_32bpp_from_32bpp_3xres_cs)},
{shaders::resolve_full_32bpp_from_64bpp_3xres_cs,
sizeof(shaders::resolve_full_32bpp_from_64bpp_3xres_cs)},
{shaders::resolve_full_64bpp_cs,
sizeof(shaders::resolve_full_64bpp_cs)},
{shaders::resolve_full_64bpp_2xres_cs,
sizeof(shaders::resolve_full_64bpp_2xres_cs)},
{shaders::resolve_full_64bpp_from_32bpp_3xres_cs,
sizeof(shaders::resolve_full_64bpp_from_32bpp_3xres_cs)},
{shaders::resolve_full_64bpp_from_64bpp_3xres_cs,
sizeof(shaders::resolve_full_64bpp_from_64bpp_3xres_cs)},
{shaders::resolve_full_128bpp_cs,
sizeof(shaders::resolve_full_128bpp_cs)},
{shaders::resolve_full_128bpp_2xres_cs,
sizeof(shaders::resolve_full_128bpp_2xres_cs)},
{shaders::resolve_full_128bpp_from_32bpp_3xres_cs,
sizeof(shaders::resolve_full_128bpp_from_32bpp_3xres_cs)},
{shaders::resolve_full_128bpp_from_64bpp_3xres_cs,
sizeof(shaders::resolve_full_128bpp_from_64bpp_3xres_cs)},
};
const uint32_t D3D12RenderTargetCache::kTransferUsedRootParameters[size_t(
@ -231,16 +248,22 @@ const D3D12RenderTargetCache::TransferModeInfo
const std::pair<const void*, size_t>
D3D12RenderTargetCache::kResolveROVClear32bppShaders[3] = {
{resolve_clear_32bpp_cs, sizeof(resolve_clear_32bpp_cs)},
{resolve_clear_32bpp_2xres_cs, sizeof(resolve_clear_32bpp_2xres_cs)},
{resolve_clear_32bpp_3xres_cs, sizeof(resolve_clear_32bpp_3xres_cs)},
{shaders::resolve_clear_32bpp_cs,
sizeof(shaders::resolve_clear_32bpp_cs)},
{shaders::resolve_clear_32bpp_2xres_cs,
sizeof(shaders::resolve_clear_32bpp_2xres_cs)},
{shaders::resolve_clear_32bpp_3xres_cs,
sizeof(shaders::resolve_clear_32bpp_3xres_cs)},
};
const std::pair<const void*, size_t>
D3D12RenderTargetCache::kResolveROVClear64bppShaders[3] = {
{resolve_clear_64bpp_cs, sizeof(resolve_clear_64bpp_cs)},
{resolve_clear_64bpp_2xres_cs, sizeof(resolve_clear_64bpp_2xres_cs)},
{resolve_clear_64bpp_3xres_cs, sizeof(resolve_clear_64bpp_3xres_cs)},
{shaders::resolve_clear_64bpp_cs,
sizeof(shaders::resolve_clear_64bpp_cs)},
{shaders::resolve_clear_64bpp_2xres_cs,
sizeof(shaders::resolve_clear_64bpp_2xres_cs)},
{shaders::resolve_clear_64bpp_3xres_cs,
sizeof(shaders::resolve_clear_64bpp_3xres_cs)},
};
D3D12RenderTargetCache::~D3D12RenderTargetCache() { Shutdown(true); }
@ -459,7 +482,7 @@ bool D3D12RenderTargetCache::Initialize() {
if (resolve_copy_shader_info.resolution_scale != resolution_scale_) {
continue;
}
const std::pair<const uint8_t*, size_t>& resolve_copy_shader =
const std::pair<const void*, size_t>& resolve_copy_shader =
kResolveCopyShaders[i];
ID3D12PipelineState* resolve_copy_pipeline =
ui::d3d12::util::CreateComputePipeline(
@ -655,8 +678,8 @@ bool D3D12RenderTargetCache::Initialize() {
// 1 sample.
host_depth_store_pipelines_[size_t(xenos::MsaaSamples::k1X)] =
ui::d3d12::util::CreateComputePipeline(
device, host_depth_store_1xmsaa_cs,
sizeof(host_depth_store_1xmsaa_cs),
device, shaders::host_depth_store_1xmsaa_cs,
sizeof(shaders::host_depth_store_1xmsaa_cs),
host_depth_store_root_signature_);
if (!host_depth_store_pipelines_[size_t(xenos::MsaaSamples::k1X)]) {
XELOGE(
@ -670,8 +693,8 @@ bool D3D12RenderTargetCache::Initialize() {
// 2 samples.
host_depth_store_pipelines_[size_t(xenos::MsaaSamples::k2X)] =
ui::d3d12::util::CreateComputePipeline(
device, host_depth_store_2xmsaa_cs,
sizeof(host_depth_store_2xmsaa_cs),
device, shaders::host_depth_store_2xmsaa_cs,
sizeof(shaders::host_depth_store_2xmsaa_cs),
host_depth_store_root_signature_);
if (!host_depth_store_pipelines_[size_t(xenos::MsaaSamples::k2X)]) {
XELOGE(
@ -685,8 +708,8 @@ bool D3D12RenderTargetCache::Initialize() {
// 4 samples.
host_depth_store_pipelines_[size_t(xenos::MsaaSamples::k4X)] =
ui::d3d12::util::CreateComputePipeline(
device, host_depth_store_4xmsaa_cs,
sizeof(host_depth_store_4xmsaa_cs),
device, shaders::host_depth_store_4xmsaa_cs,
sizeof(shaders::host_depth_store_4xmsaa_cs),
host_depth_store_root_signature_);
if (!host_depth_store_pipelines_[size_t(xenos::MsaaSamples::k4X)]) {
XELOGE(
@ -1001,10 +1024,12 @@ bool D3D12RenderTargetCache::Initialize() {
D3D12_GRAPHICS_PIPELINE_STATE_DESC uint32_rtv_clear_pipeline_desc = {};
uint32_rtv_clear_pipeline_desc.pRootSignature =
uint32_rtv_clear_root_signature_;
uint32_rtv_clear_pipeline_desc.VS.pShaderBytecode = fullscreen_vs;
uint32_rtv_clear_pipeline_desc.VS.BytecodeLength = sizeof(fullscreen_vs);
uint32_rtv_clear_pipeline_desc.PS.pShaderBytecode = clear_uint2_ps;
uint32_rtv_clear_pipeline_desc.PS.BytecodeLength = sizeof(clear_uint2_ps);
uint32_rtv_clear_pipeline_desc.VS.pShaderBytecode = shaders::fullscreen_vs;
uint32_rtv_clear_pipeline_desc.VS.BytecodeLength =
sizeof(shaders::fullscreen_vs);
uint32_rtv_clear_pipeline_desc.PS.pShaderBytecode = shaders::clear_uint2_ps;
uint32_rtv_clear_pipeline_desc.PS.BytecodeLength =
sizeof(shaders::clear_uint2_ps);
uint32_rtv_clear_pipeline_desc.BlendState.RenderTarget[0]
.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
uint32_rtv_clear_pipeline_desc.RasterizerState.FillMode =
@ -1833,9 +1858,10 @@ DXGI_FORMAT D3D12RenderTargetCache::GetColorDrawDXGIFormat(
xenos::ColorRenderTargetFormat format) const {
switch (format) {
case xenos::ColorRenderTargetFormat::k_8_8_8_8:
case xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA:
// sRGB is handled in a different way, not via the RenderTargetKey format.
return DXGI_FORMAT_R8G8B8A8_UNORM;
case xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA:
return gamma_render_target_as_srgb_ ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB
: DXGI_FORMAT_R8G8B8A8_UNORM;
case xenos::ColorRenderTargetFormat::k_16_16:
return DXGI_FORMAT_R16G16_SNORM;
case xenos::ColorRenderTargetFormat::k_16_16_16_16:
@ -1929,20 +1955,6 @@ DXGI_FORMAT D3D12RenderTargetCache::GetDepthSRVStencilDXGIFormat(
}
}
xenos::ColorRenderTargetFormat
D3D12RenderTargetCache::GetHostRelevantColorFormat(
xenos::ColorRenderTargetFormat format) const {
switch (format) {
case xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA:
// Currently handled in the shader (with incorrect blending), but even if
// handling is changed (to true sRGB), it will still be able to alias it
// with R8G8B8A8_UNORM.
return xenos::ColorRenderTargetFormat::k_8_8_8_8;
default:
return format;
}
}
RenderTargetCache::RenderTarget* D3D12RenderTargetCache::CreateRenderTarget(
RenderTargetKey key) {
ID3D12Device* device =
@ -1965,7 +1977,7 @@ RenderTargetCache::RenderTarget* D3D12RenderTargetCache::CreateRenderTarget(
assert_true(resource_desc.Format != DXGI_FORMAT_UNKNOWN);
if (resource_desc.Format == DXGI_FORMAT_UNKNOWN) {
XELOGE("D3D12RenderTargetCache: Unknown {} render target format {}",
key.is_depth ? "depth" : "color", key.host_relevant_format);
key.is_depth ? "depth" : "color", key.resource_format);
return nullptr;
}
if (key.msaa_samples == xenos::MsaaSamples::k2X && !msaa_2x_supported()) {
@ -2119,7 +2131,7 @@ RenderTargetCache::RenderTarget* D3D12RenderTargetCache::CreateRenderTarget(
descriptor_srv.GetHandle());
return new D3D12RenderTarget(
key, *this, resource.Get(), std::move(descriptor_draw),
key, resource.Get(), std::move(descriptor_draw),
std::move(descriptor_draw_srgb), std::move(descriptor_load_separate),
std::move(descriptor_srv), std::move(descriptor_srv_stencil),
resource_state);
@ -2203,16 +2215,16 @@ D3D12RenderTargetCache::GetOrCreateTransferPipelines(TransferShaderKey key) {
bool dest_is_color = (mode.output == TransferOutput::kColor);
xenos::ColorRenderTargetFormat dest_color_format =
xenos::ColorRenderTargetFormat(key.dest_host_relevant_format);
xenos::ColorRenderTargetFormat(key.dest_resource_format);
xenos::DepthRenderTargetFormat dest_depth_format =
xenos::DepthRenderTargetFormat(key.dest_host_relevant_format);
xenos::DepthRenderTargetFormat(key.dest_resource_format);
bool dest_is_64bpp =
dest_is_color && xenos::IsColorRenderTargetFormat64bpp(dest_color_format);
xenos::ColorRenderTargetFormat source_color_format =
xenos::ColorRenderTargetFormat(key.source_host_relevant_format);
xenos::ColorRenderTargetFormat(key.source_resource_format);
xenos::DepthRenderTargetFormat source_depth_format =
xenos::DepthRenderTargetFormat(key.source_host_relevant_format);
xenos::DepthRenderTargetFormat(key.source_resource_format);
// If not source_is_color, it's depth / stencil - 40-sample columns are
// swapped as opposed to color destination.
bool source_is_color = (rs & kTransferUsedRootParameterColorSRVBit) != 0;
@ -3056,10 +3068,11 @@ D3D12RenderTargetCache::GetOrCreateTransferPipelines(TransferShaderKey key) {
uint32_t source_tile_pixel_x_reg = 0;
uint32_t source_tile_pixel_y_reg = 0;
// First sample bit at 4x - horizontal sample.
// Second sample bit at 4x - vertical sample.
// At 2x, the vertical sample is either the first or the second bit
// depending on whether 2x is emulated as 4x.
// First sample bit at 4x in Direct3D 10.1+ - horizontal sample.
// Second sample bit at 4x in Direct3D 10.1+ - vertical sample.
// At 2x:
// - Native 2x: top is 1 in Direct3D 10.1+, bottom is 0.
// - 2x as 4x: top is 0, bottom is 3.
if (!source_is_64bpp && dest_is_64bpp) {
// 32bpp -> 64bpp, need two samples of the source.
@ -3088,14 +3101,15 @@ D3D12RenderTargetCache::GetOrCreateTransferPipelines(TransferShaderKey key) {
// D p0,0 s1 = S p0,0 s0,1 | S p0,0 s1,1
// D p1,0 s0 = S p1,0 s0,0 | S p1,0 s1,0
// D p1,0 s1 = S p1,0 s0,1 | S p1,0 s1,1
// Pixel index can be reused. Sample 0 should become samples 01,
// sample 1 or 3 should become samples 23.
// Pixel index can be reused. Sample 1 (for native 2x) or 0 (for 2x as
// 4x) should become samples 01, sample 0 or 3 should become samples 23.
source_sample = dxbc::Src::R(1, dxbc::Src::kZZZZ);
if (msaa_2x_supported_) {
a.OpIShL(dxbc::Dest::R(1, 0b0100), dest_sample, dxbc::Src::LU(1));
a.OpXOr(dxbc::Dest::R(1, 0b0100), dest_sample, dxbc::Src::LU(1));
a.OpIShL(dxbc::Dest::R(1, 0b0100), source_sample, dxbc::Src::LU(1));
} else {
a.OpAnd(dxbc::Dest::R(1, 0b0100), dest_sample, dxbc::Src::LU(0b10));
}
source_sample = dxbc::Src::R(1, dxbc::Src::kZZZZ);
} else {
// 32bpp -> 64bpp, 4x -> 1x.
// 1 destination horizontal pixel = 2 source horizontal samples.
@ -3176,11 +3190,14 @@ D3D12RenderTargetCache::GetOrCreateTransferPipelines(TransferShaderKey key) {
source_sample = dxbc::Src::R(1, dxbc::Src::kZZZZ);
if (key.dest_msaa_samples == xenos::MsaaSamples::k2X) {
// 64bpp -> 32bpp, 4x -> 2x.
// Destination vertical samples (first or second bit, depending on
// support) = source vertical samples (second bit).
// Destination vertical samples (1/0 in the first bit for native 2x or
// 0/1 in the second bit for 2x as 4x) = source vertical samples
// (second bit).
if (msaa_2x_supported_) {
a.OpBFI(dxbc::Dest::R(1, 0b0100), dxbc::Src::LU(1),
dxbc::Src::LU(1), dest_sample, source_sample);
a.OpXOr(dxbc::Dest::R(1, 0b0100), source_sample,
dxbc::Src::LU(1 << 1));
} else {
a.OpBFI(dxbc::Dest::R(1, 0b0100), dxbc::Src::LU(1),
dxbc::Src::LU(0), source_sample, dest_sample);
@ -3219,18 +3236,21 @@ D3D12RenderTargetCache::GetOrCreateTransferPipelines(TransferShaderKey key) {
// Same BPP, 4x -> 1x/2x.
if (key.dest_msaa_samples == xenos::MsaaSamples::k2X) {
// Same BPP, 4x -> 2x.
// Horizontal pixels to samples. Vertical sample (first or second bit,
// depending on support) to second sample bit.
// Horizontal pixels to samples. Vertical sample (1/0 in the first bit
// for native 2x or 0/1 in the second bit for 2x as 4x) to second
// sample bit.
source_sample = dxbc::Src::R(1, dxbc::Src::kZZZZ);
if (msaa_2x_supported_) {
a.OpBFI(dxbc::Dest::R(1, 0b0100), dxbc::Src::LU(31),
dxbc::Src::LU(1), dest_sample,
dxbc::Src::R(0, dxbc::Src::kXXXX));
a.OpXOr(dxbc::Dest::R(1, 0b0100), source_sample,
dxbc::Src::LU(1 << 1));
} else {
a.OpBFI(dxbc::Dest::R(1, 0b0100), dxbc::Src::LU(1),
dxbc::Src::LU(0), dxbc::Src::R(0, dxbc::Src::kXXXX),
dest_sample);
}
source_sample = dxbc::Src::R(1, dxbc::Src::kZZZZ);
a.OpUShR(dxbc::Dest::R(1, 0b0001), dxbc::Src::R(0, dxbc::Src::kXXXX),
dxbc::Src::LU(1));
source_tile_pixel_x_reg = 1;
@ -3267,10 +3287,12 @@ D3D12RenderTargetCache::GetOrCreateTransferPipelines(TransferShaderKey key) {
if (key.source_msaa_samples == xenos::MsaaSamples::k2X) {
// 2x -> 4x.
// Vertical samples (second bit) of 4x destination to vertical sample
// (01 or 03, depending on support) of 2x source.
// (1, 0 for native 2x, or 0, 3 for 2x as 4x) of 2x source.
a.OpUShR(dxbc::Dest::R(1, 0b0100), dest_sample, dxbc::Src::LU(1));
source_sample = dxbc::Src::R(1, dxbc::Src::kZZZZ);
if (!msaa_2x_supported_) {
if (msaa_2x_supported_) {
a.OpXOr(dxbc::Dest::R(1, 0b0100), source_sample, dxbc::Src::LU(1));
} else {
a.OpBFI(dxbc::Dest::R(1, 0b0100), dxbc::Src::LU(1), dxbc::Src::LU(1),
source_sample, source_sample);
}
@ -3287,12 +3309,14 @@ D3D12RenderTargetCache::GetOrCreateTransferPipelines(TransferShaderKey key) {
// 1x/2x -> different 1x/2x.
if (key.source_msaa_samples == xenos::MsaaSamples::k2X) {
// 2x -> 1x.
// Vertical pixels of 2x destination to vertical samples (01 or 03,
// depending on support) of 1x source.
// Vertical pixels of 2x destination to vertical samples (1, 0 for
// native 2x, or 0, 3 for 2x as 4x) of 1x source.
a.OpAnd(dxbc::Dest::R(1, 0b0100), dxbc::Src::R(0, dxbc::Src::kYYYY),
dxbc::Src::LU(1));
source_sample = dxbc::Src::R(1, dxbc::Src::kZZZZ);
if (!msaa_2x_supported_) {
if (msaa_2x_supported_) {
a.OpXOr(dxbc::Dest::R(1, 0b0100), source_sample, dxbc::Src::LU(1));
} else {
a.OpBFI(dxbc::Dest::R(1, 0b0100), dxbc::Src::LU(1), dxbc::Src::LU(1),
source_sample, source_sample);
}
@ -3301,15 +3325,20 @@ D3D12RenderTargetCache::GetOrCreateTransferPipelines(TransferShaderKey key) {
source_tile_pixel_y_reg = 1;
} else {
// 1x -> 2x.
// Vertical samples (first or second bit, depending on support) of 2x
// destination to vertical pixels of 1x source.
if (!msaa_2x_supported_) {
// Vertical samples (1/0 in the first bit for native 2x or 0/1 in the
// second bit for 2x as 4x) of 2x destination to vertical pixels of 1x
// source.
if (msaa_2x_supported_) {
a.OpBFI(dxbc::Dest::R(1, 0b0010), dxbc::Src::LU(31), dxbc::Src::LU(1),
dxbc::Src::R(0, dxbc::Src::kYYYY), dest_sample);
a.OpXOr(dxbc::Dest::R(1, 0b0010), dxbc::Src::R(1, dxbc::Src::kYYYY),
dxbc::Src::LU(1));
} else {
a.OpUShR(dxbc::Dest::R(1, 0b0010), dest_sample, dxbc::Src::LU(1));
a.OpBFI(dxbc::Dest::R(1, 0b0010), dxbc::Src::LU(31), dxbc::Src::LU(1),
dxbc::Src::R(0, dxbc::Src::kYYYY),
dxbc::Src::R(1, dxbc::Src::kYYYY));
}
a.OpBFI(dxbc::Dest::R(1, 0b0010), dxbc::Src::LU(31), dxbc::Src::LU(1),
dxbc::Src::R(0, dxbc::Src::kYYYY),
msaa_2x_supported_ ? dest_sample
: dxbc::Src::R(1, dxbc::Src::kYYYY));
source_tile_pixel_y_reg = 1;
}
}
@ -3910,13 +3939,15 @@ D3D12RenderTargetCache::GetOrCreateTransferPipelines(TransferShaderKey key) {
dxbc::Src::LU(1), dxbc::Src::R(0, dxbc::Src::kXXXX),
dest_sample);
}
// Vertical sample index in bit 0 for true 2x or in bit 1 for
// 4x or for 2x emulated as 4x.
// Vertical sample index as 1 or 0 in bit 0 for true 2x or as 0
// or 1 in bit 1 for 4x or for 2x emulated as 4x.
if (key.dest_msaa_samples == xenos::MsaaSamples::k2X &&
msaa_2x_supported_) {
a.OpBFI(dxbc::Dest::R(0, 0b0010), dxbc::Src::LU(31),
dxbc::Src::LU(1), dxbc::Src::R(0, dxbc::Src::kYYYY),
dest_sample);
a.OpXOr(dxbc::Dest::R(0, 0b0010),
dxbc::Src::R(0, dxbc::Src::kYYYY), dxbc::Src::LU(1));
} else {
// Using r0.w as a temporary.
a.OpUShR(dxbc::Dest::R(0, 0b1000), dest_sample,
@ -3968,19 +3999,22 @@ D3D12RenderTargetCache::GetOrCreateTransferPipelines(TransferShaderKey key) {
// 4x -> 1x/2x.
if (key.dest_msaa_samples == xenos::MsaaSamples::k2X) {
// 4x -> 2x.
// Horizontal pixels to samples. Vertical sample (first or
// second bit, depending on support) to second sample bit.
// Horizontal pixels to samples. Vertical sample (1, 0 in
// the first bit for native 2x or 0, 1 in the second bit for
// 2x as 4x) to second sample bit.
host_depth_source_sample =
dxbc::Src::R(0, dxbc::Src::kWWWW);
if (msaa_2x_supported_) {
a.OpBFI(dxbc::Dest::R(0, 0b1000), dxbc::Src::LU(31),
dxbc::Src::LU(1), dest_sample,
dxbc::Src::R(0, dxbc::Src::kXXXX));
a.OpXOr(dxbc::Dest::R(0, 0b1000),
host_depth_source_sample, dxbc::Src::LU(1 << 1));
} else {
a.OpBFI(dxbc::Dest::R(0, 0b1000), dxbc::Src::LU(1),
dxbc::Src::LU(0),
dxbc::Src::R(0, dxbc::Src::kXXXX), dest_sample);
}
host_depth_source_sample =
dxbc::Src::R(0, dxbc::Src::kWWWW);
a.OpUShR(dxbc::Dest::R(0, 0b0001),
dxbc::Src::R(0, dxbc::Src::kXXXX),
dxbc::Src::LU(1));
@ -4017,13 +4051,16 @@ D3D12RenderTargetCache::GetOrCreateTransferPipelines(TransferShaderKey key) {
xenos::MsaaSamples::k2X) {
// 2x -> 4x.
// Vertical samples (second bit) of 4x destination to
// vertical sample (01 or 03, depending on support) of 2x
// source.
// vertical sample (1, 0 for native 2x, or 0, 3 for 2x as
// 4x) of 2x source.
a.OpUShR(dxbc::Dest::R(0, 0b1000), dest_sample,
dxbc::Src::LU(1));
host_depth_source_sample =
dxbc::Src::R(0, dxbc::Src::kWWWW);
if (!msaa_2x_supported_) {
if (msaa_2x_supported_) {
a.OpXOr(dxbc::Dest::R(0, 0b1000),
host_depth_source_sample, dxbc::Src::LU(1));
} else {
a.OpBFI(dxbc::Dest::R(0, 0b1000), dxbc::Src::LU(1),
dxbc::Src::LU(1), host_depth_source_sample,
host_depth_source_sample);
@ -4045,13 +4082,17 @@ D3D12RenderTargetCache::GetOrCreateTransferPipelines(TransferShaderKey key) {
xenos::MsaaSamples::k2X) {
// 2x -> 1x.
// Vertical pixels of 2x destination to vertical samples
// (01 or 03, depending on support) of 1x source.
// (1, 0 for native 2x, or 0, 3 for 2x as 4x) of 1x
// source.
a.OpAnd(dxbc::Dest::R(0, 0b1000),
dxbc::Src::R(0, dxbc::Src::kYYYY),
dxbc::Src::LU(1));
host_depth_source_sample =
dxbc::Src::R(0, dxbc::Src::kWWWW);
if (!msaa_2x_supported_) {
if (msaa_2x_supported_) {
a.OpXOr(dxbc::Dest::R(0, 0b1000),
host_depth_source_sample, dxbc::Src::LU(1));
} else {
a.OpBFI(dxbc::Dest::R(0, 0b1000), dxbc::Src::LU(1),
dxbc::Src::LU(1), host_depth_source_sample,
host_depth_source_sample);
@ -4061,21 +4102,26 @@ D3D12RenderTargetCache::GetOrCreateTransferPipelines(TransferShaderKey key) {
dxbc::Src::LU(1));
} else {
// 1x -> 2x.
// Vertical samples (first or second bit, depending on
// support) of 2x destination to vertical pixels of 1x
// source.
// Vertical samples (1, 0 in the first bit for native 2x
// or 0, 1 in the second bit for 2x as 4x) of 2x
// destination to vertical pixels of 1x source.
// Using r0.w (not needed without source MSAA) as a
// temporary.
if (!msaa_2x_supported_) {
if (msaa_2x_supported_) {
a.OpBFI(dxbc::Dest::R(0, 0b0010), dxbc::Src::LU(31),
dxbc::Src::LU(1),
dxbc::Src::R(0, dxbc::Src::kYYYY), dest_sample);
a.OpXOr(dxbc::Dest::R(0, 0b0010),
dxbc::Src::R(0, dxbc::Src::kYYYY),
dxbc::Src::LU(1));
} else {
a.OpUShR(dxbc::Dest::R(0, 0b1000), dest_sample,
dxbc::Src::LU(1));
a.OpBFI(dxbc::Dest::R(0, 0b0010), dxbc::Src::LU(31),
dxbc::Src::LU(1),
dxbc::Src::R(0, dxbc::Src::kYYYY),
dxbc::Src::R(0, dxbc::Src::kWWWW));
}
a.OpBFI(dxbc::Dest::R(0, 0b0010), dxbc::Src::LU(31),
dxbc::Src::LU(1),
dxbc::Src::R(0, dxbc::Src::kYYYY),
msaa_2x_supported_
? dest_sample
: dxbc::Src::R(0, dxbc::Src::kWWWW));
}
}
}
@ -4315,8 +4361,8 @@ D3D12RenderTargetCache::GetOrCreateTransferPipelines(TransferShaderKey key) {
pipeline_desc.pRootSignature = transfer_root_signatures_[size_t(
use_stencil_reference_output_ ? mode.root_signature_with_stencil_ref
: mode.root_signature_no_stencil_ref)];
pipeline_desc.VS.pShaderBytecode = passthrough_position_xy_vs;
pipeline_desc.VS.BytecodeLength = sizeof(passthrough_position_xy_vs);
pipeline_desc.VS.pShaderBytecode = shaders::passthrough_position_xy_vs;
pipeline_desc.VS.BytecodeLength = sizeof(shaders::passthrough_position_xy_vs);
pipeline_desc.PS.pShaderBytecode = built_shader_.data();
pipeline_desc.PS.BytecodeLength = built_shader_size_bytes;
if (key.dest_msaa_samples == xenos::MsaaSamples::k2X && !msaa_2x_supported_) {
@ -4558,11 +4604,8 @@ void D3D12RenderTargetCache::PerformTransfersAndResolveClears(
dest_rt_key.pitch_tiles_at_32bpp;
host_depth_store_render_target_constant.resolution_scale =
resolution_scale_;
host_depth_store_render_target_constant.second_sample_index =
(dest_rt_key.msaa_samples == xenos::MsaaSamples::k2X &&
!msaa_2x_supported_)
? 3
: 1;
host_depth_store_render_target_constant.msaa_2x_supported =
uint32_t(msaa_2x_supported_);
command_list.D3DSetComputeRoot32BitConstants(
kHostDepthStoreRootParameterRenderTargetConstant,
sizeof(host_depth_store_render_target_constant) / sizeof(uint32_t),
@ -4861,8 +4904,8 @@ void D3D12RenderTargetCache::PerformTransfersAndResolveClears(
uint32_t rt_sort_index = 0;
TransferShaderKey new_transfer_shader_key;
new_transfer_shader_key.dest_msaa_samples = dest_rt_key.msaa_samples;
new_transfer_shader_key.dest_host_relevant_format =
dest_rt_key.host_relevant_format;
new_transfer_shader_key.dest_resource_format =
dest_rt_key.resource_format;
uint32_t stencil_clear_rectangle_count = 0;
for (uint32_t j = 0; j <= uint32_t(need_stencil_bit_draws); ++j) {
// j == 0 - color or depth.
@ -4899,8 +4942,8 @@ void D3D12RenderTargetCache::PerformTransfersAndResolveClears(
RenderTargetKey source_rt_key = source_d3d12_rt.key();
new_transfer_shader_key.source_msaa_samples =
source_rt_key.msaa_samples;
new_transfer_shader_key.source_host_relevant_format =
source_rt_key.host_relevant_format;
new_transfer_shader_key.source_resource_format =
source_rt_key.resource_format;
bool host_depth_source_is_copy =
host_depth_source_d3d12_rt == &dest_d3d12_rt;
new_transfer_shader_key.host_depth_source_is_copy =
@ -6013,7 +6056,7 @@ ID3D12PipelineState* D3D12RenderTargetCache::GetOrCreateDumpPipeline(
dxbc::Src::LU(xenos::kEdramTileWidthSamples >>
uint32_t(format_is_64bpp)),
dxbc::Src::VThreadIDInGroup(dxbc::Src::kXXXX));
// r0.w for 4x MSAA = pixel Y in the group
// r0.w for 2x MSAA = pixel Y in the group
a.OpUShR(dxbc::Dest::R(0, 0b1000),
dxbc::Src::VThreadIDInGroup(dxbc::Src::kYYYY), dxbc::Src::LU(1));
// r0.w = free
@ -6042,7 +6085,7 @@ ID3D12PipelineState* D3D12RenderTargetCache::GetOrCreateDumpPipeline(
if (key.msaa_samples != xenos::MsaaSamples::k1X) {
// Sample index.
// For 4x, bit 0 for horizontal, bit 1 for vertical.
// For 2x, only vertical - but 0 or 1 for true 2x MSAA or 0 or 3 for 2x MSAA
// For 2x, only vertical - but 1 or 0 for true 2x MSAA or 0 or 3 for 2x MSAA
// via two samples of 4x.
// r0.w = vertical sample index
a.OpAnd(dxbc::Dest::R(0, 0b1000),
@ -6052,11 +6095,17 @@ ID3D12PipelineState* D3D12RenderTargetCache::GetOrCreateDumpPipeline(
a.OpBFI(dxbc::Dest::R(0, 0b1000), dxbc::Src::LU(31), dxbc::Src::LU(1),
dxbc::Src::R(0, dxbc::Src::kWWWW),
dxbc::Src::VThreadIDInGroup(dxbc::Src::kXXXX));
} else if (!msaa_2x_supported_) {
// r0.w = source sample 0 or 3 for 2x MSAA emulated via 4x
a.OpBFI(dxbc::Dest::R(0, 0b1000), dxbc::Src::LU(1), dxbc::Src::LU(1),
dxbc::Src::R(0, dxbc::Src::kWWWW),
dxbc::Src::R(0, dxbc::Src::kWWWW));
} else {
if (msaa_2x_supported_) {
// r0.w = source sample 1 or 0 for native 2x MSAA
a.OpXOr(dxbc::Dest::R(0, 0b1000), dxbc::Src::R(0, dxbc::Src::kWWWW),
dxbc::Src::LU(1));
} else {
// r0.w = source sample 0 or 3 for 2x MSAA emulated via 4x
a.OpBFI(dxbc::Dest::R(0, 0b1000), dxbc::Src::LU(1), dxbc::Src::LU(1),
dxbc::Src::R(0, dxbc::Src::kWWWW),
dxbc::Src::R(0, dxbc::Src::kWWWW));
}
}
}
@ -6427,7 +6476,7 @@ void D3D12RenderTargetCache::DumpRenderTargets(uint32_t dump_base,
any_sources_32bpp_64bpp[size_t(rt_key.Is64bpp())] = true;
DumpPipelineKey pipeline_key;
pipeline_key.msaa_samples = rt_key.msaa_samples;
pipeline_key.host_relevant_format = rt_key.host_relevant_format;
pipeline_key.resource_format = rt_key.resource_format;
pipeline_key.is_depth = rt_key.is_depth;
dump_invocations_.emplace_back(rectangle, pipeline_key);
}

View File

@ -15,7 +15,9 @@
#include <cstddef>
#include <cstdint>
#include <deque>
#include <functional>
#include <memory>
#include <unordered_map>
#include <utility>
#include <vector>
@ -132,8 +134,7 @@ class D3D12RenderTargetCache final : public RenderTargetCache {
// floating-point formats, and to distinguish between two -1 representations
// in snorm formats).
D3D12RenderTarget(
RenderTargetKey key, D3D12RenderTargetCache& render_target_cache,
ID3D12Resource* resource,
RenderTargetKey key, ID3D12Resource* resource,
ui::d3d12::D3D12CpuDescriptorPool::Descriptor&& descriptor_draw,
ui::d3d12::D3D12CpuDescriptorPool::Descriptor&& descriptor_draw_srgb,
ui::d3d12::D3D12CpuDescriptorPool::Descriptor&&
@ -142,7 +143,6 @@ class D3D12RenderTargetCache final : public RenderTargetCache {
ui::d3d12::D3D12CpuDescriptorPool::Descriptor&& descriptor_srv_stencil,
D3D12_RESOURCE_STATES resource_state)
: RenderTarget(key),
render_target_cache_(render_target_cache),
resource_(resource),
descriptor_draw_(std::move(descriptor_draw)),
descriptor_draw_srgb_(std::move(descriptor_draw_srgb)),
@ -197,7 +197,6 @@ class D3D12RenderTargetCache final : public RenderTargetCache {
}
private:
D3D12RenderTargetCache& render_target_cache_;
Microsoft::WRL::ComPtr<ID3D12Resource> resource_;
ui::d3d12::D3D12CpuDescriptorPool::Descriptor descriptor_draw_;
ui::d3d12::D3D12CpuDescriptorPool::Descriptor descriptor_draw_srgb_;
@ -222,9 +221,6 @@ class D3D12RenderTargetCache final : public RenderTargetCache {
return D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION;
}
xenos::ColorRenderTargetFormat GetHostRelevantColorFormat(
xenos::ColorRenderTargetFormat format) const override;
RenderTarget* CreateRenderTarget(RenderTargetKey key) override;
bool IsHostDepthEncodingDifferent(
@ -294,7 +290,7 @@ class D3D12RenderTargetCache final : public RenderTargetCache {
// Parameter 1 - destination (shared memory or a part of it).
// Parameter 2 - source (EDRAM).
ID3D12RootSignature* resolve_copy_root_signature_ = nullptr;
static const std::pair<const uint8_t*, size_t>
static const std::pair<const void*, size_t>
kResolveCopyShaders[size_t(draw_util::ResolveCopyShaderIndex::kCount)];
ID3D12PipelineState* resolve_copy_pipelines_[size_t(
draw_util::ResolveCopyShaderIndex::kCount)] = {};
@ -416,14 +412,14 @@ class D3D12RenderTargetCache final : public RenderTargetCache {
union TransferShaderKey {
struct {
xenos::MsaaSamples dest_msaa_samples : xenos::kMsaaSamplesBits;
uint32_t dest_host_relevant_format : xenos::kRenderTargetFormatBits;
uint32_t dest_resource_format : xenos::kRenderTargetFormatBits;
xenos::MsaaSamples source_msaa_samples : xenos::kMsaaSamplesBits;
// Always 1x when host_depth_source_is_copy is true not to create the same
// pipeline for different MSAA sample counts as it doesn't matter in this
// case.
xenos::MsaaSamples host_depth_source_msaa_samples
: xenos::kMsaaSamplesBits;
uint32_t source_host_relevant_format : xenos::kRenderTargetFormatBits;
uint32_t source_resource_format : xenos::kRenderTargetFormatBits;
// If host depth is also fetched, whether it's pre-copied to the EDRAM
// buffer (but since it's just a scratch buffer, with tiles laid out
// linearly with the same pitch as in the original render target; also no
@ -537,8 +533,8 @@ class D3D12RenderTargetCache final : public RenderTargetCache {
uint32_t pitch_tiles : xenos::kEdramPitchTilesBits;
// 1 to 3.
uint32_t resolution_scale : 2;
// For native 2x MSAA vs. 2x over 4x.
uint32_t second_sample_index : 2;
// Whether 2x MSAA is supported natively rather than through 4x.
uint32_t msaa_2x_supported : 1;
};
uint32_t constant = 0;
};
@ -555,7 +551,7 @@ class D3D12RenderTargetCache final : public RenderTargetCache {
union DumpPipelineKey {
struct {
xenos::MsaaSamples msaa_samples : 2;
uint32_t host_relevant_format : 4;
uint32_t resource_format : 4;
// Last bit because this affects the root signature - after sorting, only
// change it at most once. Depth buffers have an additional stencil SRV.
uint32_t is_depth : 1;
@ -578,11 +574,11 @@ class D3D12RenderTargetCache final : public RenderTargetCache {
xenos::ColorRenderTargetFormat GetColorFormat() const {
assert_false(is_depth);
return xenos::ColorRenderTargetFormat(host_relevant_format);
return xenos::ColorRenderTargetFormat(resource_format);
}
xenos::DepthRenderTargetFormat GetDepthFormat() const {
assert_true(is_depth);
return xenos::DepthRenderTargetFormat(host_relevant_format);
return xenos::DepthRenderTargetFormat(resource_format);
}
};

View File

@ -36,6 +36,7 @@
#include "xenia/gpu/d3d12/d3d12_render_target_cache.h"
#include "xenia/gpu/draw_util.h"
#include "xenia/gpu/gpu_flags.h"
#include "xenia/gpu/xenos.h"
#include "xenia/ui/d3d12/d3d12_util.h"
DEFINE_bool(d3d12_dxbc_disasm, false,
@ -61,20 +62,22 @@ namespace xe {
namespace gpu {
namespace d3d12 {
// Generated with `xb buildhlsl`.
#include "xenia/gpu/d3d12/shaders/dxbc/adaptive_quad_hs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/adaptive_triangle_hs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/continuous_quad_hs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/continuous_triangle_hs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/discrete_quad_hs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/discrete_triangle_hs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/float24_round_ps.h"
#include "xenia/gpu/d3d12/shaders/dxbc/float24_truncate_ps.h"
#include "xenia/gpu/d3d12/shaders/dxbc/primitive_point_list_gs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/primitive_quad_list_gs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/primitive_rectangle_list_gs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/tessellation_adaptive_vs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/tessellation_indexed_vs.h"
// Generated with `xb buildshaders`.
namespace shaders {
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/adaptive_quad_hs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/adaptive_triangle_hs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/continuous_quad_hs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/continuous_triangle_hs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/discrete_quad_hs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/discrete_triangle_hs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/float24_round_ps.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/float24_truncate_ps.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/primitive_point_list_gs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/primitive_quad_list_gs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/primitive_rectangle_list_gs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/tessellation_adaptive_vs.h"
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/tessellation_indexed_vs.h"
} // namespace shaders
PipelineCache::PipelineCache(D3D12CommandProcessor& command_processor,
const RegisterFile& register_file,
@ -864,146 +867,66 @@ D3D12Shader* PipelineCache::LoadShader(xenos::ShaderType shader_type,
return shader;
}
bool PipelineCache::GetCurrentShaderModification(
DxbcShaderTranslator::Modification
PipelineCache::GetCurrentVertexShaderModification(
const Shader& shader,
DxbcShaderTranslator::Modification& modification_out) const {
Shader::HostVertexShaderType host_vertex_shader_type) const {
assert_true(shader.type() == xenos::ShaderType::kVertex);
assert_true(shader.is_ucode_analyzed());
const auto& regs = register_file_;
auto sq_program_cntl = regs.Get<reg::SQ_PROGRAM_CNTL>();
if (shader.type() == xenos::ShaderType::kVertex) {
Shader::HostVertexShaderType host_vertex_shader_type =
GetCurrentHostVertexShaderTypeIfValid();
if (host_vertex_shader_type == Shader::HostVertexShaderType(-1)) {
return false;
}
modification_out = DxbcShaderTranslator::Modification(
shader_translator_->GetDefaultVertexShaderModification(
shader.GetDynamicAddressableRegisterCount(
sq_program_cntl.vs_num_reg),
host_vertex_shader_type));
} else {
assert_true(shader.type() == xenos::ShaderType::kPixel);
DxbcShaderTranslator::Modification pixel_shader_modification(
shader_translator_->GetDefaultPixelShaderModification(
shader.GetDynamicAddressableRegisterCount(
sq_program_cntl.ps_num_reg)));
if (render_target_cache_.GetPath() ==
RenderTargetCache::Path::kHostRenderTargets) {
using DepthStencilMode =
DxbcShaderTranslator::Modification::DepthStencilMode;
RenderTargetCache::DepthFloat24Conversion depth_float24_conversion =
render_target_cache_.depth_float24_conversion();
if ((depth_float24_conversion ==
RenderTargetCache::DepthFloat24Conversion::kOnOutputTruncating ||
depth_float24_conversion ==
RenderTargetCache::DepthFloat24Conversion::kOnOutputRounding) &&
draw_util::GetDepthControlForCurrentEdramMode(regs).z_enable &&
regs.Get<reg::RB_DEPTH_INFO>().depth_format ==
xenos::DepthRenderTargetFormat::kD24FS8) {
pixel_shader_modification.pixel.depth_stencil_mode =
depth_float24_conversion ==
RenderTargetCache::DepthFloat24Conversion::
kOnOutputTruncating
? DepthStencilMode::kFloat24Truncating
: DepthStencilMode::kFloat24Rounding;
} else {
if (shader.implicit_early_z_write_allowed() &&
(!shader.writes_color_target(0) ||
!draw_util::DoesCoverageDependOnAlpha(
regs.Get<reg::RB_COLORCONTROL>()))) {
pixel_shader_modification.pixel.depth_stencil_mode =
DepthStencilMode::kEarlyHint;
} else {
pixel_shader_modification.pixel.depth_stencil_mode =
DepthStencilMode::kNoModifiers;
}
}
}
modification_out = pixel_shader_modification;
}
return true;
return DxbcShaderTranslator::Modification(
shader_translator_->GetDefaultVertexShaderModification(
shader.GetDynamicAddressableRegisterCount(sq_program_cntl.vs_num_reg),
host_vertex_shader_type));
}
Shader::HostVertexShaderType
PipelineCache::GetCurrentHostVertexShaderTypeIfValid() const {
// If the values this functions returns are changed, INVALIDATE THE SHADER
// STORAGE (increase kVersion for BOTH shaders and pipelines)! The exception
// is when the function originally returned "unsupported", but started to
// return a valid value (in this case the shader wouldn't be cached in the
// first place). Otherwise games will not be able to locate shaders for draws
// for which the host vertex shader type has changed!
DxbcShaderTranslator::Modification
PipelineCache::GetCurrentPixelShaderModification(const Shader& shader) const {
assert_true(shader.type() == xenos::ShaderType::kPixel);
assert_true(shader.is_ucode_analyzed());
const auto& regs = register_file_;
auto vgt_draw_initiator = regs.Get<reg::VGT_DRAW_INITIATOR>();
if (!xenos::IsMajorModeExplicit(vgt_draw_initiator.major_mode,
vgt_draw_initiator.prim_type)) {
// VGT_OUTPUT_PATH_CNTL and HOS registers are ignored in implicit major
// mode.
return Shader::HostVertexShaderType::kVertex;
}
if (regs.Get<reg::VGT_OUTPUT_PATH_CNTL>().path_select !=
xenos::VGTOutputPath::kTessellationEnable) {
return Shader::HostVertexShaderType::kVertex;
}
xenos::TessellationMode tessellation_mode =
regs.Get<reg::VGT_HOS_CNTL>().tess_mode;
switch (vgt_draw_initiator.prim_type) {
case xenos::PrimitiveType::kTriangleList:
// Also supported by triangle strips and fans according to:
// https://www.khronos.org/registry/OpenGL/extensions/AMD/AMD_vertex_shader_tessellator.txt
// Would need to convert those to triangle lists, but haven't seen any
// games using tessellated strips/fans so far.
switch (tessellation_mode) {
case xenos::TessellationMode::kDiscrete:
// - Call of Duty 3 - nets above barrels in the beginning of the
// first mission (turn right after the end of the intro) -
// kTriangleList.
case xenos::TessellationMode::kContinuous:
// - Viva Pinata - tree building with a beehive in the beginning
// (visible on the start screen behind the logo), waterfall in the
// beginning - kTriangleList.
return Shader::HostVertexShaderType::kTriangleDomainCPIndexed;
default:
break;
auto sq_program_cntl = regs.Get<reg::SQ_PROGRAM_CNTL>();
DxbcShaderTranslator::Modification modification(
shader_translator_->GetDefaultPixelShaderModification(
shader.GetDynamicAddressableRegisterCount(
sq_program_cntl.ps_num_reg)));
if (render_target_cache_.GetPath() ==
RenderTargetCache::Path::kHostRenderTargets) {
using DepthStencilMode =
DxbcShaderTranslator::Modification::DepthStencilMode;
RenderTargetCache::DepthFloat24Conversion depth_float24_conversion =
render_target_cache_.depth_float24_conversion();
if ((depth_float24_conversion ==
RenderTargetCache::DepthFloat24Conversion::kOnOutputTruncating ||
depth_float24_conversion ==
RenderTargetCache::DepthFloat24Conversion::kOnOutputRounding) &&
draw_util::GetDepthControlForCurrentEdramMode(regs).z_enable &&
regs.Get<reg::RB_DEPTH_INFO>().depth_format ==
xenos::DepthRenderTargetFormat::kD24FS8) {
modification.pixel.depth_stencil_mode =
depth_float24_conversion ==
RenderTargetCache::DepthFloat24Conversion::kOnOutputTruncating
? DepthStencilMode::kFloat24Truncating
: DepthStencilMode::kFloat24Rounding;
} else {
if (shader.implicit_early_z_write_allowed() &&
(!shader.writes_color_target(0) ||
!draw_util::DoesCoverageDependOnAlpha(
regs.Get<reg::RB_COLORCONTROL>()))) {
modification.pixel.depth_stencil_mode = DepthStencilMode::kEarlyHint;
} else {
modification.pixel.depth_stencil_mode = DepthStencilMode::kNoModifiers;
}
break;
case xenos::PrimitiveType::kQuadList:
switch (tessellation_mode) {
// Also supported by quad strips according to:
// https://www.khronos.org/registry/OpenGL/extensions/AMD/AMD_vertex_shader_tessellator.txt
// Would need to convert those to quad lists, but haven't seen any games
// using tessellated strips so far.
case xenos::TessellationMode::kDiscrete:
// Not seen in games so far.
case xenos::TessellationMode::kContinuous:
// - Defender - retro screen and beams in the main menu - kQuadList.
return Shader::HostVertexShaderType::kQuadDomainCPIndexed;
default:
break;
}
break;
case xenos::PrimitiveType::kTrianglePatch:
// - Banjo-Kazooie: Nuts & Bolts - water - adaptive.
// - Halo 3 - water - adaptive.
return Shader::HostVertexShaderType::kTriangleDomainPatchIndexed;
case xenos::PrimitiveType::kQuadPatch:
// - Fable II - continuous.
// - Viva Pinata - garden ground - adaptive.
return Shader::HostVertexShaderType::kQuadDomainPatchIndexed;
default:
// TODO(Triang3l): Support line patches.
break;
}
}
XELOGE(
"Unsupported tessellation mode {} for primitive type {}. Report the game "
"to Xenia developers!",
uint32_t(tessellation_mode), uint32_t(vgt_draw_initiator.prim_type));
return Shader::HostVertexShaderType(-1);
return modification;
}
bool PipelineCache::ConfigurePipeline(
D3D12Shader::D3D12Translation* vertex_shader,
D3D12Shader::D3D12Translation* pixel_shader,
xenos::PrimitiveType primitive_type, xenos::IndexFormat index_format,
const PrimitiveProcessor::ProcessingResult& primitive_processing_result,
uint32_t bound_depth_and_color_render_target_bits,
const uint32_t* bound_depth_and_color_render_target_formats,
void** pipeline_handle_out, ID3D12RootSignature** root_signature_out) {
@ -1074,7 +997,7 @@ bool PipelineCache::ConfigurePipeline(
PipelineRuntimeDescription runtime_description;
if (!GetCurrentStateDescription(
vertex_shader, pixel_shader, primitive_type, index_format,
vertex_shader, pixel_shader, primitive_processing_result,
bound_depth_and_color_render_target_bits,
bound_depth_and_color_render_target_formats, runtime_description)) {
return false;
@ -1340,7 +1263,7 @@ bool PipelineCache::TranslateAnalyzedShader(
bool PipelineCache::GetCurrentStateDescription(
D3D12Shader::D3D12Translation* vertex_shader,
D3D12Shader::D3D12Translation* pixel_shader,
xenos::PrimitiveType primitive_type, xenos::IndexFormat index_format,
const PrimitiveProcessor::ProcessingResult& primitive_processing_result,
uint32_t bound_depth_and_color_render_target_bits,
const uint32_t* bound_depth_and_color_render_target_formats,
PipelineRuntimeDescription& runtime_description_out) {
@ -1357,12 +1280,11 @@ bool PipelineCache::GetCurrentStateDescription(
// Initialize all unused fields to zero for comparison/hashing.
std::memset(&runtime_description_out, 0, sizeof(runtime_description_out));
bool tessellated =
DxbcShaderTranslator::Modification(vertex_shader->modification())
.vertex.host_vertex_shader_type !=
Shader::HostVertexShaderType::kVertex;
bool primitive_polygonal =
xenos::IsPrimitivePolygonal(tessellated, primitive_type);
assert_true(DxbcShaderTranslator::Modification(vertex_shader->modification())
.vertex.host_vertex_shader_type ==
primitive_processing_result.host_vertex_shader_type);
bool tessellated = primitive_processing_result.IsTessellated();
bool primitive_polygonal = draw_util::IsPrimitivePolygonal(regs);
bool rasterization_enabled =
draw_util::IsRasterizationPotentiallyDone(regs, primitive_polygonal);
// In Direct3D, rasterization (along with pixel counting) is disabled by
@ -1397,12 +1319,12 @@ bool PipelineCache::GetCurrentStateDescription(
description_out.vertex_shader_modification = vertex_shader->modification();
// Index buffer strip cut value.
if (pa_su_sc_mode_cntl.multi_prim_ib_ena) {
// Not using 0xFFFF with 32-bit indices because in index buffers it will be
// 0xFFFF0000 anyway due to endianness.
description_out.strip_cut_index = index_format == xenos::IndexFormat::kInt32
? PipelineStripCutIndex::kFFFFFFFF
: PipelineStripCutIndex::kFFFF;
if (primitive_processing_result.host_primitive_reset_enabled) {
description_out.strip_cut_index =
primitive_processing_result.host_index_format ==
xenos::IndexFormat::kInt16
? PipelineStripCutIndex::kFFFF
: PipelineStripCutIndex::kFFFFFFFF;
} else {
description_out.strip_cut_index = PipelineStripCutIndex::kNone;
}
@ -1410,16 +1332,15 @@ bool PipelineCache::GetCurrentStateDescription(
// Host vertex shader type and primitive topology.
if (tessellated) {
description_out.primitive_topology_type_or_tessellation_mode =
uint32_t(regs.Get<reg::VGT_HOS_CNTL>().tess_mode);
uint32_t(primitive_processing_result.tessellation_mode);
} else {
switch (primitive_type) {
switch (primitive_processing_result.host_primitive_type) {
case xenos::PrimitiveType::kPointList:
description_out.primitive_topology_type_or_tessellation_mode =
uint32_t(PipelinePrimitiveTopologyType::kPoint);
break;
case xenos::PrimitiveType::kLineList:
case xenos::PrimitiveType::kLineStrip:
case xenos::PrimitiveType::kLineLoop:
// Quads are emulated as line lists with adjacency.
case xenos::PrimitiveType::kQuadList:
case xenos::PrimitiveType::k2DLineStrip:
@ -1431,7 +1352,7 @@ bool PipelineCache::GetCurrentStateDescription(
uint32_t(PipelinePrimitiveTopologyType::kTriangle);
break;
}
switch (primitive_type) {
switch (primitive_processing_result.host_primitive_type) {
case xenos::PrimitiveType::kPointList:
description_out.geometry_shader = PipelineGeometryShader::kPointList;
break;
@ -1523,7 +1444,7 @@ bool PipelineCache::GetCurrentStateDescription(
poly_offset_scale = regs[XE_GPU_REG_PA_SU_POLY_OFFSET_BACK_SCALE].f32;
}
}
if (pa_su_sc_mode_cntl.poly_mode == xenos::PolygonModeEnable::kDisabled) {
if (pa_su_sc_mode_cntl.poly_mode != xenos::PolygonModeEnable::kDualMode) {
description_out.fill_mode_wireframe = 0;
}
} else {
@ -1536,33 +1457,18 @@ bool PipelineCache::GetCurrentStateDescription(
}
}
if (!edram_rov_used) {
// Conversion based on the calculations in Call of Duty 4 and the values it
// writes to the registers, and also on:
// https://github.com/mesa3d/mesa/blob/54ad9b444c8e73da498211870e785239ad3ff1aa/src/gallium/drivers/radeonsi/si_state.c#L943
// Dividing the scale by 2 - Call of Duty 4 sets the constant bias of
// 1/32768 for decals, however, it's done in two steps in separate places:
// first it's divided by 65536, and then it's multiplied by 2 (which is
// consistent with what si_create_rs_state does, which multiplies the offset
// by 2 if it comes from a non-D3D9 API for 24-bit depth buffers) - and
// multiplying by 2 to the number of significand bits. Tested mostly in Call
// of Duty 4 (vehicledamage map explosion decals) and Red Dead Redemption
// (shadows - 2^17 is not enough, 2^18 hasn't been tested, but 2^19
// eliminates the acne).
if (regs.Get<reg::RB_DEPTH_INFO>().depth_format ==
xenos::DepthRenderTargetFormat::kD24FS8) {
poly_offset *= float(1 << 19);
} else {
poly_offset *= float(1 << 23);
}
float poly_offset_host_scale = draw_util::GetD3D10PolygonOffsetFactor(
regs.Get<reg::RB_DEPTH_INFO>().depth_format, true);
// Using ceil here just in case a game wants the offset but passes a value
// that is too small - it's better to apply more offset than to make depth
// fighting worse or to disable the offset completely (Direct3D 12 takes an
// integer value).
description_out.depth_bias = int32_t(std::ceil(std::abs(poly_offset))) *
(poly_offset < 0.0f ? -1 : 1);
// "slope computed in subpixels (1/12 or 1/16)" - R5xx Acceleration.
description_out.depth_bias =
int32_t(std::ceil(std::abs(poly_offset * poly_offset_host_scale))) *
(poly_offset < 0.0f ? -1 : 1);
// "slope computed in subpixels ([...] 1/16)" - R5xx Acceleration.
description_out.depth_bias_slope_scaled =
poly_offset_scale * (1.0f / 16.0f);
poly_offset_scale * xenos::kPolygonOffsetScaleSubpixelUnit;
}
if (tessellated && cvars::d3d12_tessellation_wireframe) {
description_out.fill_mode_wireframe = 1;
@ -1827,16 +1733,17 @@ ID3D12PipelineState* PipelineCache::CreateD3D12Pipeline(
}
switch (description.geometry_shader) {
case PipelineGeometryShader::kPointList:
state_desc.GS.pShaderBytecode = primitive_point_list_gs;
state_desc.GS.BytecodeLength = sizeof(primitive_point_list_gs);
state_desc.GS.pShaderBytecode = shaders::primitive_point_list_gs;
state_desc.GS.BytecodeLength = sizeof(shaders::primitive_point_list_gs);
break;
case PipelineGeometryShader::kRectangleList:
state_desc.GS.pShaderBytecode = primitive_rectangle_list_gs;
state_desc.GS.BytecodeLength = sizeof(primitive_rectangle_list_gs);
state_desc.GS.pShaderBytecode = shaders::primitive_rectangle_list_gs;
state_desc.GS.BytecodeLength =
sizeof(shaders::primitive_rectangle_list_gs);
break;
case PipelineGeometryShader::kQuadList:
state_desc.GS.pShaderBytecode = primitive_quad_list_gs;
state_desc.GS.BytecodeLength = sizeof(primitive_quad_list_gs);
state_desc.GS.pShaderBytecode = shaders::primitive_quad_list_gs;
state_desc.GS.BytecodeLength = sizeof(shaders::primitive_quad_list_gs);
break;
default:
break;
@ -1846,24 +1753,25 @@ ID3D12PipelineState* PipelineCache::CreateD3D12Pipeline(
xenos::TessellationMode tessellation_mode = xenos::TessellationMode(
description.primitive_topology_type_or_tessellation_mode);
if (tessellation_mode == xenos::TessellationMode::kAdaptive) {
state_desc.VS.pShaderBytecode = tessellation_adaptive_vs;
state_desc.VS.BytecodeLength = sizeof(tessellation_adaptive_vs);
state_desc.VS.pShaderBytecode = shaders::tessellation_adaptive_vs;
state_desc.VS.BytecodeLength = sizeof(shaders::tessellation_adaptive_vs);
} else {
state_desc.VS.pShaderBytecode = tessellation_indexed_vs;
state_desc.VS.BytecodeLength = sizeof(tessellation_indexed_vs);
state_desc.VS.pShaderBytecode = shaders::tessellation_indexed_vs;
state_desc.VS.BytecodeLength = sizeof(shaders::tessellation_indexed_vs);
}
switch (tessellation_mode) {
case xenos::TessellationMode::kDiscrete:
switch (host_vertex_shader_type) {
case Shader::HostVertexShaderType::kTriangleDomainCPIndexed:
case Shader::HostVertexShaderType::kTriangleDomainPatchIndexed:
state_desc.HS.pShaderBytecode = discrete_triangle_hs;
state_desc.HS.BytecodeLength = sizeof(discrete_triangle_hs);
state_desc.HS.pShaderBytecode = shaders::discrete_triangle_hs;
state_desc.HS.BytecodeLength =
sizeof(shaders::discrete_triangle_hs);
break;
case Shader::HostVertexShaderType::kQuadDomainCPIndexed:
case Shader::HostVertexShaderType::kQuadDomainPatchIndexed:
state_desc.HS.pShaderBytecode = discrete_quad_hs;
state_desc.HS.BytecodeLength = sizeof(discrete_quad_hs);
state_desc.HS.pShaderBytecode = shaders::discrete_quad_hs;
state_desc.HS.BytecodeLength = sizeof(shaders::discrete_quad_hs);
break;
default:
assert_unhandled_case(host_vertex_shader_type);
@ -1874,13 +1782,14 @@ ID3D12PipelineState* PipelineCache::CreateD3D12Pipeline(
switch (host_vertex_shader_type) {
case Shader::HostVertexShaderType::kTriangleDomainCPIndexed:
case Shader::HostVertexShaderType::kTriangleDomainPatchIndexed:
state_desc.HS.pShaderBytecode = continuous_triangle_hs;
state_desc.HS.BytecodeLength = sizeof(continuous_triangle_hs);
state_desc.HS.pShaderBytecode = shaders::continuous_triangle_hs;
state_desc.HS.BytecodeLength =
sizeof(shaders::continuous_triangle_hs);
break;
case Shader::HostVertexShaderType::kQuadDomainCPIndexed:
case Shader::HostVertexShaderType::kQuadDomainPatchIndexed:
state_desc.HS.pShaderBytecode = continuous_quad_hs;
state_desc.HS.BytecodeLength = sizeof(continuous_quad_hs);
state_desc.HS.pShaderBytecode = shaders::continuous_quad_hs;
state_desc.HS.BytecodeLength = sizeof(shaders::continuous_quad_hs);
break;
default:
assert_unhandled_case(host_vertex_shader_type);
@ -1890,12 +1799,13 @@ ID3D12PipelineState* PipelineCache::CreateD3D12Pipeline(
case xenos::TessellationMode::kAdaptive:
switch (host_vertex_shader_type) {
case Shader::HostVertexShaderType::kTriangleDomainPatchIndexed:
state_desc.HS.pShaderBytecode = adaptive_triangle_hs;
state_desc.HS.BytecodeLength = sizeof(adaptive_triangle_hs);
state_desc.HS.pShaderBytecode = shaders::adaptive_triangle_hs;
state_desc.HS.BytecodeLength =
sizeof(shaders::adaptive_triangle_hs);
break;
case Shader::HostVertexShaderType::kQuadDomainPatchIndexed:
state_desc.HS.pShaderBytecode = adaptive_quad_hs;
state_desc.HS.BytecodeLength = sizeof(adaptive_quad_hs);
state_desc.HS.pShaderBytecode = shaders::adaptive_quad_hs;
state_desc.HS.BytecodeLength = sizeof(shaders::adaptive_quad_hs);
break;
default:
assert_unhandled_case(host_vertex_shader_type);
@ -1933,12 +1843,12 @@ ID3D12PipelineState* PipelineCache::CreateD3D12Pipeline(
description.depth_format == xenos::DepthRenderTargetFormat::kD24FS8) {
switch (render_target_cache_.depth_float24_conversion()) {
case RenderTargetCache::DepthFloat24Conversion::kOnOutputTruncating:
state_desc.PS.pShaderBytecode = float24_truncate_ps;
state_desc.PS.BytecodeLength = sizeof(float24_truncate_ps);
state_desc.PS.pShaderBytecode = shaders::float24_truncate_ps;
state_desc.PS.BytecodeLength = sizeof(shaders::float24_truncate_ps);
break;
case RenderTargetCache::DepthFloat24Conversion::kOnOutputRounding:
state_desc.PS.pShaderBytecode = float24_round_ps;
state_desc.PS.BytecodeLength = sizeof(float24_round_ps);
state_desc.PS.pShaderBytecode = shaders::float24_round_ps;
state_desc.PS.BytecodeLength = sizeof(shaders::float24_round_ps);
break;
default:
break;

View File

@ -29,6 +29,7 @@
#include "xenia/gpu/d3d12/d3d12_shader.h"
#include "xenia/gpu/dxbc_shader_translator.h"
#include "xenia/gpu/gpu_flags.h"
#include "xenia/gpu/primitive_processor.h"
#include "xenia/gpu/register_file.h"
#include "xenia/gpu/xenos.h"
#include "xenia/ui/d3d12/d3d12_api.h"
@ -67,18 +68,21 @@ class PipelineCache {
shader.AnalyzeUcode(ucode_disasm_buffer_);
}
// Retrieves the shader modification for the current state, and returns
// whether it is valid. The shader must have microcode analyzed.
bool PipelineCache::GetCurrentShaderModification(
// Retrieves the shader modification for the current state. The shader must
// have microcode analyzed.
DxbcShaderTranslator::Modification
PipelineCache::GetCurrentVertexShaderModification(
const Shader& shader,
DxbcShaderTranslator::Modification& modification_out) const;
Shader::HostVertexShaderType host_vertex_shader_type) const;
DxbcShaderTranslator::Modification
PipelineCache::GetCurrentPixelShaderModification(const Shader& shader) const;
// If draw_util::IsRasterizationPotentiallyDone is false, the pixel shader
// MUST be made nullptr BEFORE calling this!
bool ConfigurePipeline(
D3D12Shader::D3D12Translation* vertex_shader,
D3D12Shader::D3D12Translation* pixel_shader,
xenos::PrimitiveType primitive_type, xenos::IndexFormat index_format,
const PrimitiveProcessor::ProcessingResult& primitive_processing_result,
uint32_t bound_depth_and_color_render_target_bits,
const uint32_t* bound_depth_and_color_render_targets_formats,
void** pipeline_handle_out, ID3D12RootSignature** root_signature_out);
@ -226,10 +230,6 @@ class PipelineCache {
PipelineDescription description;
};
// Returns the host vertex shader type for the current draw if it's valid and
// supported, or Shader::HostVertexShaderType(-1) if not.
Shader::HostVertexShaderType GetCurrentHostVertexShaderTypeIfValid() const;
D3D12Shader* LoadShader(xenos::ShaderType shader_type,
const uint32_t* host_address, uint32_t dword_count,
uint64_t data_hash);
@ -247,7 +247,7 @@ class PipelineCache {
bool GetCurrentStateDescription(
D3D12Shader::D3D12Translation* vertex_shader,
D3D12Shader::D3D12Translation* pixel_shader,
xenos::PrimitiveType primitive_type, xenos::IndexFormat index_format,
const PrimitiveProcessor::ProcessingResult& primitive_processing_result,
uint32_t bound_depth_and_color_render_target_bits,
const uint32_t* bound_depth_and_color_render_target_formats,
PipelineRuntimeDescription& runtime_description_out);

View File

@ -16,7 +16,7 @@ project("xenia-gpu-d3d12")
})
local_platform_files()
files({
"shaders/bin/*.h",
"../shaders/bytecode/d3d12_5_1/*.h",
})
group("src")

View File

@ -1,762 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2018 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/gpu/d3d12/primitive_converter.h"
#include <algorithm>
#include "xenia/base/assert.h"
#include "xenia/base/cvar.h"
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
#include "xenia/base/memory.h"
#include "xenia/base/platform.h"
#include "xenia/base/profiling.h"
#include "xenia/gpu/d3d12/d3d12_command_processor.h"
#include "xenia/ui/d3d12/d3d12_util.h"
DEFINE_bool(d3d12_convert_quads_to_triangles, false,
"Convert quad lists to triangle lists on the CPU instead of using "
"a geometry shader. Not recommended for playing, for debugging "
"primarily (because PIX fails to display vertices when a geometry "
"shader is used), and this way quads can't be discarded correctly "
"when the game uses vertex kill functionality.",
"D3D12");
namespace xe {
namespace gpu {
namespace d3d12 {
PrimitiveConverter::PrimitiveConverter(D3D12CommandProcessor& command_processor,
const RegisterFile& register_file,
Memory& memory,
TraceWriter& trace_writer)
: command_processor_(command_processor),
register_file_(register_file),
memory_(memory),
trace_writer_(trace_writer) {
system_page_size_ = uint32_t(memory::page_size());
}
PrimitiveConverter::~PrimitiveConverter() { Shutdown(); }
bool PrimitiveConverter::Initialize() {
auto& provider = command_processor_.GetD3D12Context().GetD3D12Provider();
auto device = provider.GetDevice();
D3D12_HEAP_FLAGS heap_flag_create_not_zeroed =
provider.GetHeapFlagCreateNotZeroed();
// There can be at most 65535 indices in a Xenos draw call (16 bit index
// count), but they can be up to 4 bytes large, and conversion can add more
// indices (almost triple the count for triangle strips or fans, for
// instance).
buffer_pool_ = std::make_unique<ui::d3d12::D3D12UploadBufferPool>(
provider, std::max(sizeof(uint32_t) * 3 * 65535,
ui::d3d12::D3D12UploadBufferPool::kDefaultPageSize));
// Create the static index buffer for non-indexed drawing.
D3D12_RESOURCE_DESC static_ib_desc;
ui::d3d12::util::FillBufferResourceDesc(
static_ib_desc, kStaticIBTotalCount * sizeof(uint16_t),
D3D12_RESOURCE_FLAG_NONE);
if (FAILED(device->CreateCommittedResource(
&ui::d3d12::util::kHeapPropertiesUpload, heap_flag_create_not_zeroed,
&static_ib_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr,
IID_PPV_ARGS(&static_ib_upload_)))) {
XELOGE(
"Failed to create the upload buffer for the primitive conversion "
"static index buffer");
Shutdown();
return false;
}
D3D12_RANGE static_ib_read_range;
static_ib_read_range.Begin = 0;
static_ib_read_range.End = 0;
void* static_ib_mapping;
if (FAILED(static_ib_upload_->Map(0, &static_ib_read_range,
&static_ib_mapping))) {
XELOGE(
"Failed to map the upload buffer for the primitive conversion "
"static index buffer");
Shutdown();
return false;
}
uint16_t* static_ib_data = reinterpret_cast<uint16_t*>(static_ib_mapping);
// Triangle fans as triangle lists.
// https://docs.microsoft.com/en-us/windows/desktop/direct3d9/triangle-fans
// Ordered as (v1, v2, v0), (v2, v3, v0).
uint16_t* static_ib_data_pointer =
&static_ib_data[kStaticIBTriangleFanOffset];
for (uint32_t i = 2; i < kMaxNonIndexedVertices; ++i) {
*(static_ib_data_pointer++) = i - 1;
*(static_ib_data_pointer++) = i;
*(static_ib_data_pointer++) = 0;
}
static_ib_data_pointer = &static_ib_data[kStaticIBQuadOffset];
for (uint32_t i = 0; i < (kMaxNonIndexedVertices >> 2); ++i) {
uint32_t quad_index = i << 2;
*(static_ib_data_pointer++) = quad_index;
*(static_ib_data_pointer++) = quad_index + 1;
*(static_ib_data_pointer++) = quad_index + 2;
*(static_ib_data_pointer++) = quad_index;
*(static_ib_data_pointer++) = quad_index + 2;
*(static_ib_data_pointer++) = quad_index + 3;
}
static_ib_upload_->Unmap(0, nullptr);
// Not uploaded yet.
static_ib_upload_submission_ = UINT64_MAX;
if (FAILED(device->CreateCommittedResource(
&ui::d3d12::util::kHeapPropertiesDefault, heap_flag_create_not_zeroed,
&static_ib_desc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr,
IID_PPV_ARGS(&static_ib_)))) {
XELOGE("Failed to create the primitive conversion static index buffer");
Shutdown();
return false;
}
static_ib_gpu_address_ = static_ib_->GetGPUVirtualAddress();
memory_regions_invalidated_.store(0ull, std::memory_order_relaxed);
memory_invalidation_callback_handle_ =
memory_.RegisterPhysicalMemoryInvalidationCallback(
MemoryInvalidationCallbackThunk, this);
return true;
}
void PrimitiveConverter::Shutdown() {
if (memory_invalidation_callback_handle_ != nullptr) {
memory_.UnregisterPhysicalMemoryInvalidationCallback(
memory_invalidation_callback_handle_);
memory_invalidation_callback_handle_ = nullptr;
}
ui::d3d12::util::ReleaseAndNull(static_ib_);
ui::d3d12::util::ReleaseAndNull(static_ib_upload_);
buffer_pool_.reset();
}
void PrimitiveConverter::ClearCache() { buffer_pool_->ClearCache(); }
void PrimitiveConverter::CompletedSubmissionUpdated() {
if (static_ib_upload_ && command_processor_.GetCompletedSubmission() >=
static_ib_upload_submission_) {
// Completely uploaded - release the upload buffer.
static_ib_upload_->Release();
static_ib_upload_ = nullptr;
}
}
void PrimitiveConverter::BeginSubmission() {
// Got a command list now - upload and transition the static index buffer if
// needed.
if (static_ib_upload_ && static_ib_upload_submission_ == UINT64_MAX) {
command_processor_.GetDeferredCommandList().D3DCopyResource(
static_ib_, static_ib_upload_);
command_processor_.PushTransitionBarrier(static_ib_,
D3D12_RESOURCE_STATE_COPY_DEST,
D3D12_RESOURCE_STATE_INDEX_BUFFER);
static_ib_upload_submission_ = command_processor_.GetCurrentSubmission();
}
}
void PrimitiveConverter::BeginFrame() {
buffer_pool_->Reclaim(command_processor_.GetCompletedFrame());
converted_indices_cache_.clear();
memory_regions_used_ = 0;
}
xenos::PrimitiveType PrimitiveConverter::GetReplacementPrimitiveType(
xenos::PrimitiveType type) {
switch (type) {
case xenos::PrimitiveType::kTriangleFan:
return xenos::PrimitiveType::kTriangleList;
case xenos::PrimitiveType::kLineLoop:
return xenos::PrimitiveType::kLineStrip;
case xenos::PrimitiveType::kQuadList:
if (cvars::d3d12_convert_quads_to_triangles) {
return xenos::PrimitiveType::kTriangleList;
}
break;
default:
break;
}
return type;
}
PrimitiveConverter::ConversionResult PrimitiveConverter::ConvertPrimitives(
xenos::PrimitiveType source_type, uint32_t address, uint32_t index_count,
xenos::IndexFormat index_format, xenos::Endian index_endianness,
D3D12_GPU_VIRTUAL_ADDRESS& gpu_address_out, uint32_t& index_count_out) {
bool index_32bit = index_format == xenos::IndexFormat::kInt32;
const auto& regs = register_file_;
bool reset = regs.Get<reg::PA_SU_SC_MODE_CNTL>().multi_prim_ib_ena;
// Swap the reset index because we will be comparing unswapped values to it.
uint32_t reset_index = xenos::GpuSwap(
regs[XE_GPU_REG_VGT_MULTI_PRIM_IB_RESET_INDX].u32, index_endianness);
// If the specified reset index is the same as the one used by Direct3D 12
// (0xFFFF or 0xFFFFFFFF - in the pipeline cache, we use the former for
// 16-bit and the latter for 32-bit indices), we can use the buffer directly.
uint32_t reset_index_host = index_32bit ? 0xFFFFFFFFu : 0xFFFFu;
// Degenerate line loops are just lines.
if (source_type == xenos::PrimitiveType::kLineLoop && index_count <= 2) {
source_type = xenos::PrimitiveType::kLineStrip;
}
// Check if need to convert at all.
if (source_type == xenos::PrimitiveType::kTriangleStrip ||
source_type == xenos::PrimitiveType::kLineStrip) {
if (!reset || reset_index == reset_index_host) {
return ConversionResult::kConversionNotNeeded;
}
} else if (source_type == xenos::PrimitiveType::kQuadList) {
if (!cvars::d3d12_convert_quads_to_triangles) {
return ConversionResult::kConversionNotNeeded;
}
} else if (source_type != xenos::PrimitiveType::kTriangleFan &&
source_type != xenos::PrimitiveType::kLineLoop) {
return ConversionResult::kConversionNotNeeded;
}
#if XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES
SCOPE_profile_cpu_f("gpu");
#endif // XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES
// Exit early for clearly empty draws, without even reading the memory.
uint32_t index_count_min;
if (source_type == xenos::PrimitiveType::kLineStrip ||
source_type == xenos::PrimitiveType::kLineLoop) {
index_count_min = 2;
} else if (source_type == xenos::PrimitiveType::kQuadList) {
index_count_min = 4;
} else {
index_count_min = 3;
}
if (index_count < index_count_min) {
return ConversionResult::kPrimitiveEmpty;
}
// Invalidate the cache if data behind any entry was modified.
if (memory_regions_invalidated_.exchange(0ull, std::memory_order_acquire) &
memory_regions_used_) {
converted_indices_cache_.clear();
memory_regions_used_ = 0;
}
address &= index_32bit ? 0x1FFFFFFC : 0x1FFFFFFE;
uint32_t index_size = index_32bit ? sizeof(uint32_t) : sizeof(uint16_t);
uint32_t index_buffer_size = index_size * index_count;
uint32_t address_last = address + index_size * (index_count - 1);
// Create the cache entry, currently only for the key.
ConvertedIndices converted_indices;
converted_indices.key.address = address;
converted_indices.key.source_type = source_type;
converted_indices.key.format = index_format;
converted_indices.key.count = index_count;
converted_indices.key.reset = reset ? 1 : 0;
converted_indices.reset_index = reset_index;
// Try to find the previously converted index buffer.
auto found_range =
converted_indices_cache_.equal_range(converted_indices.key.value);
for (auto iter = found_range.first; iter != found_range.second; ++iter) {
const ConvertedIndices& found_converted = iter->second;
if (reset && found_converted.reset_index != reset_index) {
continue;
}
if (found_converted.converted_index_count == 0) {
return ConversionResult::kPrimitiveEmpty;
}
if (!found_converted.gpu_address) {
return ConversionResult::kConversionNotNeeded;
}
gpu_address_out = found_converted.gpu_address;
index_count_out = found_converted.converted_index_count;
return ConversionResult::kConverted;
}
// Get the memory usage mask for cache invalidation.
// 1 bit = (512 / 64) MB = 8 MB.
uint64_t memory_regions_used_bits = ~((1ull << (address >> 23)) - 1);
if (address_last < (63 << 23)) {
memory_regions_used_bits = (1ull << ((address_last >> 23) + 1)) - 1;
}
union {
const void* source;
const uint8_t* source_8;
const uint16_t* source_16;
const uint32_t* source_32;
uintptr_t source_uintptr;
};
source = memory_.TranslatePhysical(address);
// Calculate the new index count, and also check if there's nothing to convert
// in the buffer (for instance, if not using actually primitive reset).
uint32_t converted_index_count = 0;
bool conversion_needed = false;
bool simd = false;
// Optimization specific to primitive types - if reset index not found in the
// source index buffer, can set this to false and use a faster way of copying.
bool reset_actually_used = reset;
if (source_type == xenos::PrimitiveType::kTriangleFan) {
// Triangle fans are not supported by Direct3D 12 at all.
conversion_needed = true;
trace_writer_.WriteMemoryRead(address, index_buffer_size);
if (reset) {
uint32_t current_fan_index_count = 0;
for (uint32_t i = 0; i < index_count; ++i) {
uint32_t index = index_format == xenos::IndexFormat::kInt32
? source_32[i]
: source_16[i];
if (index == reset_index) {
current_fan_index_count = 0;
continue;
}
if (++current_fan_index_count >= 3) {
converted_index_count += 3;
}
}
} else {
converted_index_count = 3 * (index_count - 2);
}
} else if (source_type == xenos::PrimitiveType::kTriangleStrip ||
source_type == xenos::PrimitiveType::kLineStrip) {
converted_index_count = index_count;
// Check if the restart index is used at all in this buffer because reading
// vertices from a default heap is faster than from an upload heap.
conversion_needed = false;
trace_writer_.WriteMemoryRead(address, index_buffer_size);
#if XE_ARCH_AMD64
// Will use SIMD to copy 16-byte blocks using _mm_or_si128.
simd = true;
union {
const void* check_source;
const uint32_t* check_source_16;
const uint32_t* check_source_32;
const __m128i* check_source_128;
uintptr_t check_source_uintptr;
};
check_source = source;
uint32_t check_indices_remaining = index_count;
alignas(16) uint64_t check_result[2];
if (index_format == xenos::IndexFormat::kInt32) {
while (check_indices_remaining != 0 && (check_source_uintptr & 15)) {
--check_indices_remaining;
if (*(check_source_32++) == reset_index) {
conversion_needed = true;
check_indices_remaining = 0;
}
}
__m128i check_reset_index_vector = _mm_set1_epi32(reset_index);
while (check_indices_remaining >= 4) {
check_indices_remaining -= 4;
_mm_store_si128(reinterpret_cast<__m128i*>(&check_result),
_mm_cmpeq_epi32(_mm_load_si128(check_source_128++),
check_reset_index_vector));
if (check_result[0] || check_result[1]) {
conversion_needed = true;
check_indices_remaining = 0;
}
}
while (check_indices_remaining != 0) {
--check_indices_remaining;
if (*(check_source_32++) == reset_index) {
conversion_needed = true;
check_indices_remaining = 0;
}
}
} else {
while (check_indices_remaining != 0 && (check_source_uintptr & 15)) {
--check_indices_remaining;
if (*(check_source_16++) == reset_index) {
conversion_needed = true;
check_indices_remaining = 0;
}
}
__m128i check_reset_index_vector = _mm_set1_epi16(reset_index);
while (check_indices_remaining >= 8) {
check_indices_remaining -= 8;
_mm_store_si128(reinterpret_cast<__m128i*>(&check_result),
_mm_cmpeq_epi16(_mm_load_si128(check_source_128++),
check_reset_index_vector));
if (check_result[0] || check_result[1]) {
conversion_needed = true;
check_indices_remaining = 0;
}
}
while (check_indices_remaining != 0) {
--check_indices_remaining;
if (*(check_source_16++) == reset_index) {
conversion_needed = true;
check_indices_remaining = 0;
}
}
}
#else
if (index_format == xenos::IndexFormat::kInt32) {
for (uint32_t i = 0; i < index_count; ++i) {
if (source_32[i] == reset_index) {
conversion_needed = true;
break;
}
}
} else {
for (uint32_t i = 0; i < index_count; ++i) {
if (source_16[i] == reset_index) {
conversion_needed = true;
break;
}
}
}
#endif // XE_ARCH_AMD64
} else if (source_type == xenos::PrimitiveType::kLineLoop) {
conversion_needed = true;
trace_writer_.WriteMemoryRead(address, index_buffer_size);
if (reset) {
reset_actually_used = false;
uint32_t current_strip_index_count = 0;
for (uint32_t i = 0; i < index_count; ++i) {
uint32_t index = index_format == xenos::IndexFormat::kInt32
? source_32[i]
: source_16[i];
if (index == reset_index) {
reset_actually_used = true;
// Loop strips with more than 2 vertices.
if (current_strip_index_count > 2) {
++converted_index_count;
}
current_strip_index_count = 0;
continue;
}
// Start a new strip if 2 vertices, add one vertex if more.
if (++current_strip_index_count >= 2) {
converted_index_count += current_strip_index_count == 2 ? 2 : 1;
}
}
} else {
converted_index_count = index_count + 1;
}
} else if (source_type == xenos::PrimitiveType::kQuadList) {
conversion_needed = true;
trace_writer_.WriteMemoryRead(address, index_buffer_size);
converted_index_count = (index_count >> 2) * 6;
}
converted_indices.converted_index_count = converted_index_count;
// If nothing to convert, store this result so the check won't be happening
// again and again and exit.
if (!conversion_needed || converted_index_count == 0) {
converted_indices.gpu_address = 0;
converted_indices_cache_.emplace(converted_indices.key.value,
converted_indices);
memory_regions_used_ |= memory_regions_used_bits;
return converted_index_count == 0 ? ConversionResult::kPrimitiveEmpty
: ConversionResult::kConversionNotNeeded;
}
// Convert.
D3D12_GPU_VIRTUAL_ADDRESS gpu_address;
void* target = AllocateIndices(index_format, converted_index_count,
simd ? address & 15 : 0, gpu_address);
if (target == nullptr) {
return ConversionResult::kFailed;
}
if (source_type == xenos::PrimitiveType::kTriangleFan) {
// https://docs.microsoft.com/en-us/windows/desktop/direct3d9/triangle-fans
// Ordered as (v1, v2, v0), (v2, v3, v0).
if (reset) {
uint32_t current_fan_index_count = 0;
uint32_t current_fan_first_index = 0;
if (index_format == xenos::IndexFormat::kInt32) {
uint32_t* target_32 = reinterpret_cast<uint32_t*>(target);
for (uint32_t i = 0; i < index_count; ++i) {
uint32_t index = source_32[i];
if (index == reset_index) {
current_fan_index_count = 0;
continue;
}
if (current_fan_index_count == 0) {
current_fan_first_index = index;
}
if (++current_fan_index_count >= 3) {
*(target_32++) = source_32[i - 1];
*(target_32++) = index;
*(target_32++) = current_fan_first_index;
}
}
} else {
uint16_t* target_16 = reinterpret_cast<uint16_t*>(target);
for (uint32_t i = 0; i < index_count; ++i) {
uint16_t index = source_16[i];
if (index == reset_index) {
current_fan_index_count = 0;
continue;
}
if (current_fan_index_count == 0) {
current_fan_first_index = index;
}
if (++current_fan_index_count >= 3) {
*(target_16++) = source_16[i - 1];
*(target_16++) = index;
*(target_16++) = uint16_t(current_fan_first_index);
}
}
}
} else {
if (index_format == xenos::IndexFormat::kInt32) {
uint32_t* target_32 = reinterpret_cast<uint32_t*>(target);
for (uint32_t i = 2; i < index_count; ++i) {
*(target_32++) = source_32[i - 1];
*(target_32++) = source_32[i];
*(target_32++) = source_32[0];
}
} else {
uint16_t* target_16 = reinterpret_cast<uint16_t*>(target);
for (uint32_t i = 2; i < index_count; ++i) {
*(target_16++) = source_16[i - 1];
*(target_16++) = source_16[i];
*(target_16++) = source_16[0];
}
}
}
} else if (source_type == xenos::PrimitiveType::kTriangleStrip ||
source_type == xenos::PrimitiveType::kLineStrip) {
#if XE_ARCH_AMD64
// Replace the reset index with the maximum representable value - vector OR
// gives 0 or 0xFFFF/0xFFFFFFFF, which is exactly what is needed.
// Allocations in the target index buffer are aligned with 16-byte
// granularity, and within 16-byte vectors, both the source and the target
// start at the same offset.
union {
const __m128i* source_aligned_128;
uintptr_t source_aligned_uintptr;
};
source_aligned_uintptr = source_uintptr & ~(uintptr_t(15));
union {
__m128i* target_aligned_128;
uintptr_t target_aligned_uintptr;
};
target_aligned_uintptr =
reinterpret_cast<uintptr_t>(target) & ~(uintptr_t(15));
uint32_t vector_count = (address_last >> 4) - (address >> 4) + 1;
if (index_format == xenos::IndexFormat::kInt32) {
__m128i reset_index_vector = _mm_set1_epi32(reset_index);
for (uint32_t i = 0; i < vector_count; ++i) {
__m128i indices_vector = _mm_load_si128(source_aligned_128++);
__m128i indices_are_reset_vector =
_mm_cmpeq_epi32(indices_vector, reset_index_vector);
_mm_store_si128(target_aligned_128++,
_mm_or_si128(indices_vector, indices_are_reset_vector));
}
} else {
__m128i reset_index_vector = _mm_set1_epi16(reset_index);
for (uint32_t i = 0; i < vector_count; ++i) {
__m128i indices_vector = _mm_load_si128(source_aligned_128++);
__m128i indices_are_reset_vector =
_mm_cmpeq_epi16(indices_vector, reset_index_vector);
_mm_store_si128(target_aligned_128++,
_mm_or_si128(indices_vector, indices_are_reset_vector));
}
}
#else
if (index_format == xenos::IndexFormat::kInt32) {
for (uint32_t i = 0; i < index_count; ++i) {
uint32_t index = source_32[i];
reinterpret_cast<uint32_t*>(target)[i] =
index == reset_index ? 0xFFFFFFFFu : index;
}
} else {
for (uint32_t i = 0; i < index_count; ++i) {
uint16_t index = source_16[i];
reinterpret_cast<uint16_t*>(target)[i] =
index == reset_index ? 0xFFFFu : index;
}
}
#endif // XE_ARCH_AMD64
} else if (source_type == xenos::PrimitiveType::kLineLoop) {
if (reset_actually_used) {
uint32_t current_strip_index_count = 0;
uint32_t current_strip_first_index = 0;
if (index_format == xenos::IndexFormat::kInt32) {
uint32_t* target_32 = reinterpret_cast<uint32_t*>(target);
for (uint32_t i = 0; i < index_count; ++i) {
uint32_t index = source_32[i];
if (index == reset_index) {
if (current_strip_index_count > 2) {
*(target_32++) = current_strip_first_index;
}
current_strip_index_count = 0;
continue;
}
if (current_strip_index_count == 0) {
current_strip_first_index = index;
}
++current_strip_index_count;
if (current_strip_index_count >= 2) {
if (current_strip_index_count == 2) {
*(target_32++) = current_strip_first_index;
}
*(target_32++) = index;
}
}
} else {
uint16_t* target_16 = reinterpret_cast<uint16_t*>(target);
for (uint32_t i = 0; i < index_count; ++i) {
uint16_t index = source_16[i];
if (index == reset_index) {
if (current_strip_index_count > 2) {
*(target_16++) = uint16_t(current_strip_first_index);
}
current_strip_index_count = 0;
continue;
}
if (current_strip_index_count == 0) {
current_strip_first_index = index;
}
++current_strip_index_count;
if (current_strip_index_count >= 2) {
if (current_strip_index_count == 2) {
*(target_16++) = uint16_t(current_strip_first_index);
}
*(target_16++) = index;
}
}
}
} else {
std::memcpy(target, source, index_count * index_size);
if (converted_index_count > index_count) {
if (index_format == xenos::IndexFormat::kInt32) {
reinterpret_cast<uint32_t*>(target)[index_count] = source_32[0];
} else {
reinterpret_cast<uint16_t*>(target)[index_count] = source_16[0];
}
}
}
} else if (source_type == xenos::PrimitiveType::kQuadList) {
uint32_t quad_count = index_count >> 4;
if (index_format == xenos::IndexFormat::kInt32) {
uint32_t* target_32 = reinterpret_cast<uint32_t*>(target);
for (uint32_t i = 0; i < quad_count; ++i) {
uint32_t quad_index = i << 2;
*(target_32++) = source_32[quad_index];
*(target_32++) = source_32[quad_index + 1];
*(target_32++) = source_32[quad_index + 2];
*(target_32++) = source_32[quad_index];
*(target_32++) = source_32[quad_index + 2];
*(target_32++) = source_32[quad_index + 3];
}
} else {
uint16_t* target_16 = reinterpret_cast<uint16_t*>(target);
for (uint32_t i = 0; i < quad_count; ++i) {
uint32_t quad_index = i << 2;
*(target_16++) = source_16[quad_index];
*(target_16++) = source_16[quad_index + 1];
*(target_16++) = source_16[quad_index + 2];
*(target_16++) = source_16[quad_index];
*(target_16++) = source_16[quad_index + 2];
*(target_16++) = source_16[quad_index + 3];
}
}
}
// Cache and return the indices.
converted_indices.gpu_address = gpu_address;
converted_indices_cache_.emplace(converted_indices.key.value,
converted_indices);
memory_regions_used_ |= memory_regions_used_bits;
gpu_address_out = gpu_address;
index_count_out = converted_index_count;
return ConversionResult::kConverted;
}
void* PrimitiveConverter::AllocateIndices(
xenos::IndexFormat format, uint32_t count, uint32_t simd_offset,
D3D12_GPU_VIRTUAL_ADDRESS& gpu_address_out) {
if (count == 0) {
return nullptr;
}
uint32_t size =
count * (format == xenos::IndexFormat::kInt32 ? sizeof(uint32_t)
: sizeof(uint16_t));
// 16-align all index data because SIMD is used to replace the reset index
// (without that, 4-alignment would be required anyway to mix 16-bit and
// 32-bit indices in one buffer page).
size = xe::align(size, uint32_t(16));
// Add some space to align SIMD register components the same way in the source
// and the buffer.
simd_offset &= 15;
if (simd_offset != 0) {
size += 16;
}
D3D12_GPU_VIRTUAL_ADDRESS gpu_address;
uint8_t* mapping =
buffer_pool_->Request(command_processor_.GetCurrentFrame(), size, 16,
nullptr, nullptr, &gpu_address);
if (mapping == nullptr) {
XELOGE("Failed to allocate space for {} converted {}-bit vertex indices",
count, format == xenos::IndexFormat::kInt32 ? 32 : 16);
return nullptr;
}
gpu_address_out = gpu_address + simd_offset;
return mapping + simd_offset;
}
std::pair<uint32_t, uint32_t> PrimitiveConverter::MemoryInvalidationCallback(
uint32_t physical_address_start, uint32_t length, bool exact_range) {
// 1 bit = (512 / 64) MB = 8 MB. Invalidate a region of this size.
uint32_t bit_index_first = physical_address_start >> 23;
uint32_t bit_index_last = (physical_address_start + length - 1) >> 23;
uint64_t bits = ~((1ull << bit_index_first) - 1);
if (bit_index_last < 63) {
bits &= (1ull << (bit_index_last + 1)) - 1;
}
memory_regions_invalidated_ |= bits;
return std::make_pair<uint32_t, uint32_t>(0, UINT32_MAX);
}
std::pair<uint32_t, uint32_t>
PrimitiveConverter::MemoryInvalidationCallbackThunk(
void* context_ptr, uint32_t physical_address_start, uint32_t length,
bool exact_range) {
return reinterpret_cast<PrimitiveConverter*>(context_ptr)
->MemoryInvalidationCallback(physical_address_start, length, exact_range);
}
D3D12_GPU_VIRTUAL_ADDRESS PrimitiveConverter::GetStaticIndexBuffer(
xenos::PrimitiveType source_type, uint32_t index_count,
uint32_t& index_count_out) const {
if (index_count > kMaxNonIndexedVertices) {
assert_always();
return D3D12_GPU_VIRTUAL_ADDRESS(0);
}
if (source_type == xenos::PrimitiveType::kTriangleFan) {
index_count_out = (std::max(index_count, uint32_t(2)) - 2) * 3;
return static_ib_gpu_address_ +
kStaticIBTriangleFanOffset * sizeof(uint16_t);
}
if (source_type == xenos::PrimitiveType::kQuadList &&
cvars::d3d12_convert_quads_to_triangles) {
index_count_out = (index_count >> 2) * 6;
return static_ib_gpu_address_ + kStaticIBQuadOffset * sizeof(uint16_t);
}
return D3D12_GPU_VIRTUAL_ADDRESS(0);
}
void PrimitiveConverter::InitializeTrace() {
// WriteMemoryRead must not be skipped.
converted_indices_cache_.clear();
memory_regions_used_ = 0;
}
} // namespace d3d12
} // namespace gpu
} // namespace xe

View File

@ -1,189 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2018 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_GPU_D3D12_PRIMITIVE_CONVERTER_H_
#define XENIA_GPU_D3D12_PRIMITIVE_CONVERTER_H_
#include <atomic>
#include <memory>
#include <unordered_map>
#include "xenia/gpu/register_file.h"
#include "xenia/gpu/trace_writer.h"
#include "xenia/gpu/xenos.h"
#include "xenia/memory.h"
#include "xenia/ui/d3d12/d3d12_context.h"
#include "xenia/ui/d3d12/d3d12_upload_buffer_pool.h"
namespace xe {
namespace gpu {
namespace d3d12 {
class D3D12CommandProcessor;
// Index buffer cache for primitive types not natively supported by Direct3D 12:
// - Triangle and line strips with non-0xFFFF/0xFFFFFFFF reset index.
// - Triangle fans.
// - Line loops (only indexed ones - non-indexed are better handled in vertex
// shaders, otherwise a whole index buffer would have to be created for every
// vertex count value).
// - Quad lists (for debugging since geometry shaders break PIX - as an
// alternative to the geometry shader).
class PrimitiveConverter {
public:
PrimitiveConverter(D3D12CommandProcessor& command_processor,
const RegisterFile& register_file, Memory& memory,
TraceWriter& trace_writer);
~PrimitiveConverter();
bool Initialize();
void Shutdown();
void ClearCache();
void CompletedSubmissionUpdated();
void BeginSubmission();
void BeginFrame();
// Returns the primitive type that the original type will be converted to.
static xenos::PrimitiveType GetReplacementPrimitiveType(
xenos::PrimitiveType type);
enum class ConversionResult {
// Converted to a transient buffer.
kConverted,
// Conversion not required - use the index buffer in shared memory.
kConversionNotNeeded,
// No errors, but nothing to render.
kPrimitiveEmpty,
// Total failure of the draw call.
kFailed
};
// Converts an index buffer to the primitive type returned by
// GetReplacementPrimitiveType. If conversion has been performed, the returned
// buffer will be in the GENERIC_READ state (it's in an upload heap). Only
// writing to the outputs if returning kConverted. The restart index will be
// handled internally from the register values.
ConversionResult ConvertPrimitives(xenos::PrimitiveType source_type,
uint32_t address, uint32_t index_count,
xenos::IndexFormat index_format,
xenos::Endian index_endianness,
D3D12_GPU_VIRTUAL_ADDRESS& gpu_address_out,
uint32_t& index_count_out);
// Returns the 16-bit index buffer for drawing unsupported non-indexed
// primitives in INDEX_BUFFER state, for non-indexed drawing. Returns 0 if
// conversion is not available (can draw natively).
D3D12_GPU_VIRTUAL_ADDRESS GetStaticIndexBuffer(
xenos::PrimitiveType source_type, uint32_t index_count,
uint32_t& index_count_out) const;
// Callback for invalidating buffers mid-frame.
std::pair<uint32_t, uint32_t> MemoryInvalidationCallback(
uint32_t physical_address_start, uint32_t length, bool exact_range);
void InitializeTrace();
private:
// simd_offset is source address & 15 - if SIMD is used, the source and the
// target must have the same alignment within one register. 0 is optimal when
// not using SIMD.
void* AllocateIndices(xenos::IndexFormat format, uint32_t count,
uint32_t simd_offset,
D3D12_GPU_VIRTUAL_ADDRESS& gpu_address_out);
static std::pair<uint32_t, uint32_t> MemoryInvalidationCallbackThunk(
void* context_ptr, uint32_t physical_address_start, uint32_t length,
bool exact_range);
D3D12CommandProcessor& command_processor_;
const RegisterFile& register_file_;
Memory& memory_;
TraceWriter& trace_writer_;
std::unique_ptr<ui::d3d12::D3D12UploadBufferPool> buffer_pool_;
// Static index buffers for emulating unsupported primitive types when drawing
// without an index buffer.
// CPU-side, used only for uploading - destroyed once the copy commands have
// been completed.
ID3D12Resource* static_ib_upload_ = nullptr;
uint64_t static_ib_upload_submission_;
// GPU-side - used for drawing.
ID3D12Resource* static_ib_ = nullptr;
D3D12_GPU_VIRTUAL_ADDRESS static_ib_gpu_address_;
// In PM4 draw packets, 16 bits are used for the vertex count.
static constexpr uint32_t kMaxNonIndexedVertices = 65535;
static constexpr uint32_t kStaticIBTriangleFanOffset = 0;
static constexpr uint32_t kStaticIBTriangleFanCount =
(kMaxNonIndexedVertices - 2) * 3;
static constexpr uint32_t kStaticIBQuadOffset =
kStaticIBTriangleFanOffset + kStaticIBTriangleFanCount;
static constexpr uint32_t kStaticIBQuadCount =
(kMaxNonIndexedVertices >> 2) * 6;
static constexpr uint32_t kStaticIBTotalCount =
kStaticIBQuadOffset + kStaticIBQuadCount;
// Not identifying the index buffer uniquely - reset index must also be
// checked if reset is enabled.
union ConvertedIndicesKey {
uint64_t value;
struct {
uint32_t address; // 32
xenos::PrimitiveType source_type : 6; // 38
xenos::IndexFormat format : 1; // 39
uint32_t count : 16; // 55
uint32_t reset : 1; // 56
};
// Clearing the unused bits.
ConvertedIndicesKey() : value(0) {}
ConvertedIndicesKey(const ConvertedIndicesKey& key) : value(key.value) {}
ConvertedIndicesKey& operator=(const ConvertedIndicesKey& key) {
value = key.value;
return *this;
}
bool operator==(const ConvertedIndicesKey& key) const {
return value == key.value;
}
bool operator!=(const ConvertedIndicesKey& key) const {
return value != key.value;
}
};
struct ConvertedIndices {
ConvertedIndicesKey key;
// If reset is enabled, this also must be checked to find cached indices.
uint32_t reset_index;
// Zero GPU address if conversion not needed or the resulting index buffer
// is empty.
D3D12_GPU_VIRTUAL_ADDRESS gpu_address;
// When conversion is not needed, this must be equal to the original index
// count.
uint32_t converted_index_count;
};
// Cache for a single frame.
std::unordered_multimap<uint64_t, ConvertedIndices> converted_indices_cache_;
// Very coarse cache invalidation - if something is modified in a 8 MB portion
// of the physical memory and converted indices are also there, invalidate all
// the cache.
uint64_t memory_regions_used_;
std::atomic<uint64_t> memory_regions_invalidated_ = 0;
void* memory_invalidation_callback_handle_ = nullptr;
uint32_t system_page_size_;
};
} // namespace d3d12
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_D3D12_PRIMITIVE_CONVERTER_H_

View File

@ -1,104 +0,0 @@
// generated from `xb buildhlsl`
// source: adaptive_quad.hs.hlsl
const uint8_t adaptive_quad_hs[] = {
0x44, 0x58, 0x42, 0x43, 0x5B, 0x72, 0xC4, 0x5D, 0x23, 0xC3, 0xD2, 0x31,
0x9A, 0x46, 0xA8, 0xF4, 0x08, 0x84, 0x19, 0xF6, 0x01, 0x00, 0x00, 0x00,
0xB0, 0x04, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
0xA4, 0x00, 0x00, 0x00, 0xDC, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00,
0xD4, 0x01, 0x00, 0x00, 0x14, 0x04, 0x00, 0x00, 0x52, 0x44, 0x45, 0x46,
0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x01, 0x05, 0x53, 0x48,
0x00, 0x05, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x13, 0x13, 0x44, 0x25,
0x3C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
0x28, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66,
0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4C, 0x53, 0x4C, 0x20, 0x53,
0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6F, 0x6D, 0x70, 0x69, 0x6C,
0x65, 0x72, 0x20, 0x31, 0x30, 0x2E, 0x31, 0x00, 0x49, 0x53, 0x47, 0x4E,
0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
0x58, 0x45, 0x54, 0x45, 0x53, 0x53, 0x46, 0x41, 0x43, 0x54, 0x4F, 0x52,
0x00, 0xAB, 0xAB, 0xAB, 0x4F, 0x53, 0x47, 0x4E, 0x2C, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x0E, 0x00, 0x00, 0x58, 0x45, 0x56, 0x45,
0x52, 0x54, 0x45, 0x58, 0x49, 0x44, 0x00, 0xAB, 0x50, 0x43, 0x53, 0x47,
0xBC, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0E, 0x00, 0x00,
0x98, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x0E, 0x00, 0x00,
0x98, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x0E, 0x00, 0x00,
0x98, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x0E, 0x00, 0x00,
0xA6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x0E, 0x00, 0x00,
0xA6, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x0E, 0x00, 0x00,
0x53, 0x56, 0x5F, 0x54, 0x65, 0x73, 0x73, 0x46, 0x61, 0x63, 0x74, 0x6F,
0x72, 0x00, 0x53, 0x56, 0x5F, 0x49, 0x6E, 0x73, 0x69, 0x64, 0x65, 0x54,
0x65, 0x73, 0x73, 0x46, 0x61, 0x63, 0x74, 0x6F, 0x72, 0x00, 0xAB, 0xAB,
0x53, 0x48, 0x45, 0x58, 0x38, 0x02, 0x00, 0x00, 0x51, 0x00, 0x03, 0x00,
0x8E, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x01, 0x93, 0x20, 0x00, 0x01,
0x94, 0x20, 0x00, 0x01, 0x95, 0x18, 0x00, 0x01, 0x96, 0x20, 0x00, 0x01,
0x97, 0x18, 0x00, 0x01, 0x6A, 0x08, 0x00, 0x01, 0x72, 0x00, 0x00, 0x01,
0x65, 0x00, 0x00, 0x03, 0x12, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
0x36, 0x00, 0x00, 0x05, 0x12, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x01,
0x73, 0x00, 0x00, 0x01, 0x99, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x00,
0x5F, 0x00, 0x00, 0x02, 0x00, 0x70, 0x01, 0x00, 0x5F, 0x00, 0x00, 0x04,
0x12, 0x90, 0x21, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x67, 0x00, 0x00, 0x04, 0x12, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0B, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x04, 0x12, 0x20, 0x10, 0x00,
0x01, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x04,
0x12, 0x20, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00,
0x67, 0x00, 0x00, 0x04, 0x12, 0x20, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00,
0x0E, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00,
0x5B, 0x00, 0x00, 0x04, 0x12, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x06, 0x12, 0x00, 0x10, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0A, 0x70, 0x01, 0x00, 0x01, 0x40, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x07, 0x12, 0x00, 0x10, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x40, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x04,
0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x70, 0x01, 0x00,
0x36, 0x00, 0x00, 0x08, 0x12, 0x20, 0x90, 0x00, 0x1A, 0x00, 0x10, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0A, 0x90, 0xA1, 0x00, 0x0A, 0x00, 0x10, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x01,
0x73, 0x00, 0x00, 0x01, 0x99, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00,
0x5F, 0x00, 0x00, 0x02, 0x00, 0x70, 0x01, 0x00, 0x5F, 0x00, 0x00, 0x04,
0x12, 0x90, 0x21, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x67, 0x00, 0x00, 0x04, 0x12, 0x20, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x04, 0x12, 0x20, 0x10, 0x00,
0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x02,
0x01, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x04, 0x12, 0x20, 0x10, 0x00,
0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x06,
0x12, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x70, 0x01, 0x00,
0x01, 0x40, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x09,
0x12, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x10, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x40, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x04,
0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x70, 0x01, 0x00,
0x33, 0x00, 0x00, 0x0C, 0x12, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0A, 0x90, 0xE1, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x10, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x90, 0xA1, 0x00,
0x0A, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x36, 0x00, 0x00, 0x04, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0A, 0x70, 0x01, 0x00, 0x36, 0x00, 0x00, 0x07, 0x12, 0x20, 0xD0, 0x00,
0x04, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0A, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x01,
0x53, 0x54, 0x41, 0x54, 0x94, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};

View File

@ -1,81 +0,0 @@
//
// Generated by Microsoft (R) HLSL Shader Compiler 10.1
//
//
//
// Patch Constant signature:
//
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------- ------
// SV_TessFactor 0 x 0 QUADEDGE float x
// SV_TessFactor 1 x 1 QUADEDGE float x
// SV_TessFactor 2 x 2 QUADEDGE float x
// SV_TessFactor 3 x 3 QUADEDGE float x
// SV_InsideTessFactor 0 x 4 QUADINT float x
// SV_InsideTessFactor 1 x 5 QUADINT float x
//
//
// Input signature:
//
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------- ------
// XETESSFACTOR 0 x 0 NONE float x
//
//
// Output signature:
//
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------- ------
// XEVERTEXID 0 x 0 NONE float x
//
// Tessellation Domain # of control points
// -------------------- --------------------
// Quadrilateral 4
//
// Tessellation Output Primitive Partitioning Type
// ------------------------------ ------------------
// Clockwise Triangles Even Fractional
//
hs_5_1
hs_decls
dcl_input_control_point_count 4
dcl_output_control_point_count 4
dcl_tessellator_domain domain_quad
dcl_tessellator_partitioning partitioning_fractional_even
dcl_tessellator_output_primitive output_triangle_cw
dcl_globalFlags refactoringAllowed
hs_control_point_phase
dcl_output o0.x
mov o0.x, l(0)
ret
hs_fork_phase
dcl_hs_fork_phase_instance_count 4
dcl_input vForkInstanceID
dcl_input vicp[4][0].x
dcl_output_siv o0.x, finalQuadUeq0EdgeTessFactor
dcl_output_siv o1.x, finalQuadVeq0EdgeTessFactor
dcl_output_siv o2.x, finalQuadUeq1EdgeTessFactor
dcl_output_siv o3.x, finalQuadVeq1EdgeTessFactor
dcl_temps 1
dcl_indexrange o0.x 4
iadd r0.x, vForkInstanceID.x, l(3)
and r0.x, r0.x, l(3)
mov r0.y, vForkInstanceID.x
mov o[r0.y + 0].x, vicp[r0.x + 0][0].x
ret
hs_fork_phase
dcl_hs_fork_phase_instance_count 2
dcl_input vForkInstanceID
dcl_input vicp[4][0].x
dcl_output_siv o4.x, finalQuadUInsideTessFactor
dcl_output_siv o5.x, finalQuadVInsideTessFactor
dcl_temps 1
dcl_indexrange o4.x 2
ult r0.x, vForkInstanceID.x, l(1)
movc r0.x, r0.x, l(0), l(3)
ineg r0.y, vForkInstanceID.x
min r0.x, vicp[r0.y + 2][0].x, vicp[r0.x + 0][0].x
mov r0.y, vForkInstanceID.x
mov o[r0.y + 4].x, r0.x
ret
// Approximately 14 instruction slots used

View File

@ -1,87 +0,0 @@
// generated from `xb buildhlsl`
// source: adaptive_triangle.hs.hlsl
const uint8_t adaptive_triangle_hs[] = {
0x44, 0x58, 0x42, 0x43, 0x0A, 0xA5, 0x75, 0xB0, 0x13, 0x0C, 0x82, 0x6C,
0xAB, 0x68, 0xC3, 0xA1, 0x34, 0xFB, 0x63, 0xC1, 0x01, 0x00, 0x00, 0x00,
0xE0, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
0xA4, 0x00, 0x00, 0x00, 0xDC, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00,
0xA4, 0x01, 0x00, 0x00, 0x44, 0x03, 0x00, 0x00, 0x52, 0x44, 0x45, 0x46,
0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x01, 0x05, 0x53, 0x48,
0x00, 0x05, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x13, 0x13, 0x44, 0x25,
0x3C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
0x28, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66,
0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4C, 0x53, 0x4C, 0x20, 0x53,
0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6F, 0x6D, 0x70, 0x69, 0x6C,
0x65, 0x72, 0x20, 0x31, 0x30, 0x2E, 0x31, 0x00, 0x49, 0x53, 0x47, 0x4E,
0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
0x58, 0x45, 0x54, 0x45, 0x53, 0x53, 0x46, 0x41, 0x43, 0x54, 0x4F, 0x52,
0x00, 0xAB, 0xAB, 0xAB, 0x4F, 0x53, 0x47, 0x4E, 0x2C, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x0E, 0x00, 0x00, 0x58, 0x45, 0x56, 0x45,
0x52, 0x54, 0x45, 0x58, 0x49, 0x44, 0x00, 0xAB, 0x50, 0x43, 0x53, 0x47,
0x8C, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0E, 0x00, 0x00,
0x68, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x0E, 0x00, 0x00,
0x68, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x0E, 0x00, 0x00,
0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x0E, 0x00, 0x00,
0x53, 0x56, 0x5F, 0x54, 0x65, 0x73, 0x73, 0x46, 0x61, 0x63, 0x74, 0x6F,
0x72, 0x00, 0x53, 0x56, 0x5F, 0x49, 0x6E, 0x73, 0x69, 0x64, 0x65, 0x54,
0x65, 0x73, 0x73, 0x46, 0x61, 0x63, 0x74, 0x6F, 0x72, 0x00, 0xAB, 0xAB,
0x53, 0x48, 0x45, 0x58, 0x98, 0x01, 0x00, 0x00, 0x51, 0x00, 0x03, 0x00,
0x66, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x01, 0x93, 0x18, 0x00, 0x01,
0x94, 0x18, 0x00, 0x01, 0x95, 0x10, 0x00, 0x01, 0x96, 0x20, 0x00, 0x01,
0x97, 0x18, 0x00, 0x01, 0x6A, 0x08, 0x00, 0x01, 0x72, 0x00, 0x00, 0x01,
0x65, 0x00, 0x00, 0x03, 0x12, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
0x36, 0x00, 0x00, 0x05, 0x12, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x01,
0x73, 0x00, 0x00, 0x01, 0x99, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00,
0x5F, 0x00, 0x00, 0x02, 0x00, 0x70, 0x01, 0x00, 0x5F, 0x00, 0x00, 0x04,
0x12, 0x90, 0x21, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x67, 0x00, 0x00, 0x04, 0x12, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
0x11, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x04, 0x12, 0x20, 0x10, 0x00,
0x01, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x04,
0x12, 0x20, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
0x68, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x04,
0x12, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x1E, 0x00, 0x00, 0x06, 0x12, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0A, 0x70, 0x01, 0x00, 0x01, 0x40, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x4E, 0x00, 0x00, 0x08, 0x00, 0xD0, 0x00, 0x00, 0x12, 0x00, 0x10, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x40, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x04,
0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x70, 0x01, 0x00,
0x36, 0x00, 0x00, 0x08, 0x12, 0x20, 0x90, 0x00, 0x1A, 0x00, 0x10, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0A, 0x90, 0xA1, 0x00, 0x0A, 0x00, 0x10, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x01,
0x73, 0x00, 0x00, 0x01, 0x5F, 0x00, 0x00, 0x04, 0x12, 0x90, 0x21, 0x00,
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x04,
0x12, 0x20, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
0x68, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x09,
0x12, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x90, 0x21, 0x00,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x90, 0x21, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x08,
0x12, 0x20, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x10, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0A, 0x90, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x01, 0x53, 0x54, 0x41, 0x54,
0x94, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};

View File

@ -1,70 +0,0 @@
//
// Generated by Microsoft (R) HLSL Shader Compiler 10.1
//
//
//
// Patch Constant signature:
//
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------- ------
// SV_TessFactor 0 x 0 TRIEDGE float x
// SV_TessFactor 1 x 1 TRIEDGE float x
// SV_TessFactor 2 x 2 TRIEDGE float x
// SV_InsideTessFactor 0 x 3 TRIINT float x
//
//
// Input signature:
//
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------- ------
// XETESSFACTOR 0 x 0 NONE float x
//
//
// Output signature:
//
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------- ------
// XEVERTEXID 0 x 0 NONE float x
//
// Tessellation Domain # of control points
// -------------------- --------------------
// Triangle 3
//
// Tessellation Output Primitive Partitioning Type
// ------------------------------ ------------------
// Clockwise Triangles Even Fractional
//
hs_5_1
hs_decls
dcl_input_control_point_count 3
dcl_output_control_point_count 3
dcl_tessellator_domain domain_tri
dcl_tessellator_partitioning partitioning_fractional_even
dcl_tessellator_output_primitive output_triangle_cw
dcl_globalFlags refactoringAllowed
hs_control_point_phase
dcl_output o0.x
mov o0.x, l(0)
ret
hs_fork_phase
dcl_hs_fork_phase_instance_count 3
dcl_input vForkInstanceID
dcl_input vicp[3][0].x
dcl_output_siv o0.x, finalTriUeq0EdgeTessFactor
dcl_output_siv o1.x, finalTriVeq0EdgeTessFactor
dcl_output_siv o2.x, finalTriWeq0EdgeTessFactor
dcl_temps 1
dcl_indexrange o0.x 3
iadd r0.x, vForkInstanceID.x, l(1)
udiv null, r0.x, r0.x, l(3)
mov r0.y, vForkInstanceID.x
mov o[r0.y + 0].x, vicp[r0.x + 0][0].x
ret
hs_fork_phase
dcl_input vicp[3][0].x
dcl_output_siv o3.x, finalTriInsideTessFactor
dcl_temps 1
min r0.x, vicp[2][0].x, vicp[1][0].x
min o3.x, r0.x, vicp[0][0].x
ret
// Approximately 10 instruction slots used

View File

@ -1,59 +0,0 @@
// generated from `xb buildhlsl`
// source: clear_uint2.ps.hlsl
const uint8_t clear_uint2_ps[] = {
0x44, 0x58, 0x42, 0x43, 0x5A, 0x4F, 0x43, 0x0F, 0x11, 0xAF, 0xD2, 0xAA,
0xBD, 0xDE, 0xD1, 0xE4, 0x3E, 0x1F, 0x99, 0xF6, 0x01, 0x00, 0x00, 0x00,
0x94, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
0x58, 0x01, 0x00, 0x00, 0x68, 0x01, 0x00, 0x00, 0x9C, 0x01, 0x00, 0x00,
0xF8, 0x01, 0x00, 0x00, 0x52, 0x44, 0x45, 0x46, 0x1C, 0x01, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x3C, 0x00, 0x00, 0x00, 0x01, 0x05, 0xFF, 0xFF, 0x00, 0x05, 0x00, 0x00,
0xF4, 0x00, 0x00, 0x00, 0x13, 0x13, 0x44, 0x25, 0x3C, 0x00, 0x00, 0x00,
0x18, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
0x24, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x58, 0x65, 0x43, 0x6C, 0x65, 0x61, 0x72, 0x43,
0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74, 0x73, 0x00, 0xAB, 0xAB, 0xAB,
0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00,
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x63, 0x6C, 0x65, 0x61, 0x72,
0x5F, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x00, 0x75, 0x69, 0x6E, 0x74, 0x32,
0x00, 0xAB, 0xAB, 0xAB, 0x01, 0x00, 0x13, 0x00, 0x01, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xC7, 0x00, 0x00, 0x00, 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66,
0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4C, 0x53, 0x4C, 0x20, 0x53,
0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6F, 0x6D, 0x70, 0x69, 0x6C,
0x65, 0x72, 0x20, 0x31, 0x30, 0x2E, 0x31, 0x00, 0x49, 0x53, 0x47, 0x4E,
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x4F, 0x53, 0x47, 0x4E, 0x2C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x0C, 0x00, 0x00, 0x53, 0x56, 0x5F, 0x54, 0x61, 0x72, 0x67, 0x65,
0x74, 0x00, 0xAB, 0xAB, 0x53, 0x48, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00,
0x51, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x6A, 0x08, 0x00, 0x01,
0x59, 0x00, 0x00, 0x07, 0x46, 0x8E, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x03, 0x32, 0x20, 0x10, 0x00,
0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x07, 0x32, 0x20, 0x10, 0x00,
0x00, 0x00, 0x00, 0x00, 0x46, 0x80, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x01,
0x53, 0x54, 0x41, 0x54, 0x94, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};

View File

@ -1,41 +0,0 @@
//
// Generated by Microsoft (R) HLSL Shader Compiler 10.1
//
//
// Buffer Definitions:
//
// cbuffer XeClearConstants
// {
//
// uint2 xe_clear_value; // Offset: 0 Size: 8
//
// }
//
//
// Resource Bindings:
//
// Name Type Format Dim ID HLSL Bind Count
// ------------------------------ ---------- ------- ----------- ------- -------------- ------
// XeClearConstants cbuffer NA NA CB0 cb0 1
//
//
//
// Input signature:
//
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------- ------
// no Input
//
// Output signature:
//
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------- ------
// SV_Target 0 xy 0 TARGET uint xy
//
ps_5_1
dcl_globalFlags refactoringAllowed
dcl_constantbuffer CB0[0:0][1], immediateIndexed, space=0
dcl_output o0.xy
mov o0.xy, CB0[0][0].xyxx
ret
// Approximately 2 instruction slots used

View File

@ -1,298 +0,0 @@
// generated from `xb buildhlsl`
// source: continuous_quad.hs.hlsl
const uint8_t continuous_quad_hs[] = {
0x44, 0x58, 0x42, 0x43, 0xCE, 0xF4, 0xB8, 0x73, 0x74, 0x35, 0xD9, 0xE6,
0x42, 0x49, 0xF7, 0xF8, 0xDD, 0x91, 0xC9, 0xCD, 0x01, 0x00, 0x00, 0x00,
0xC0, 0x0D, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
0x8C, 0x0A, 0x00, 0x00, 0xC0, 0x0A, 0x00, 0x00, 0xF4, 0x0A, 0x00, 0x00,
0xB8, 0x0B, 0x00, 0x00, 0x24, 0x0D, 0x00, 0x00, 0x52, 0x44, 0x45, 0x46,
0x4C, 0x0A, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x01, 0x05, 0x53, 0x48,
0x00, 0x05, 0x00, 0x00, 0x22, 0x0A, 0x00, 0x00, 0x13, 0x13, 0x44, 0x25,
0x3C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
0x28, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x73,
0x79, 0x73, 0x74, 0x65, 0x6D, 0x5F, 0x63, 0x62, 0x75, 0x66, 0x66, 0x65,
0x72, 0x00, 0xAB, 0xAB, 0x64, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00,
0x90, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x68, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x05, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x9C, 0x05, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0xC0, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
0xE4, 0x05, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x78, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0xFF, 0x05, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x05, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x16, 0x06, 0x00, 0x00,
0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x30, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
0x54, 0x06, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xC0, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x62, 0x06, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x05, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x78, 0x06, 0x00, 0x00,
0x28, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xC0, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
0x8F, 0x06, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xAC, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0xD0, 0x06, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00,
0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0x06, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x08, 0x07, 0x00, 0x00,
0x9C, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x78, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
0x29, 0x07, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xE4, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x37, 0x07, 0x00, 0x00, 0xAC, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x05, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x47, 0x07, 0x00, 0x00,
0xB0, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x68, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
0x8C, 0x07, 0x00, 0x00, 0xD0, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x78, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0xA1, 0x07, 0x00, 0x00, 0xD4, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBC, 0x07, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00,
0xDC, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
0x24, 0x08, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x38, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x5C, 0x08, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x05, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x6D, 0x08, 0x00, 0x00,
0xF4, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x78, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
0x82, 0x08, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xC0, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x97, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x05, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xB2, 0x08, 0x00, 0x00,
0x08, 0x01, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xC0, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
0xCC, 0x08, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x78, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0xE7, 0x08, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x08, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x09, 0x00, 0x00,
0x40, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x3C, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
0x60, 0x09, 0x00, 0x00, 0x50, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x3C, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x79, 0x09, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8C, 0x09, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xB0, 0x09, 0x00, 0x00,
0xA0, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xC8, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
0xEC, 0x09, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x3C, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x0A, 0x0A, 0x00, 0x00, 0xD0, 0x01, 0x00, 0x00,
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x08, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x66,
0x6C, 0x61, 0x67, 0x73, 0x00, 0x64, 0x77, 0x6F, 0x72, 0x64, 0x00, 0xAB,
0x00, 0x00, 0x13, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0x05, 0x00, 0x00,
0x78, 0x65, 0x5F, 0x74, 0x65, 0x73, 0x73, 0x65, 0x6C, 0x6C, 0x61, 0x74,
0x69, 0x6F, 0x6E, 0x5F, 0x66, 0x61, 0x63, 0x74, 0x6F, 0x72, 0x5F, 0x72,
0x61, 0x6E, 0x67, 0x65, 0x00, 0x66, 0x6C, 0x6F, 0x61, 0x74, 0x32, 0x00,
0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB9, 0x05, 0x00, 0x00,
0x78, 0x65, 0x5F, 0x6C, 0x69, 0x6E, 0x65, 0x5F, 0x6C, 0x6F, 0x6F, 0x70,
0x5F, 0x63, 0x6C, 0x6F, 0x73, 0x69, 0x6E, 0x67, 0x5F, 0x69, 0x6E, 0x64,
0x65, 0x78, 0x00, 0x78, 0x65, 0x5F, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78,
0x5F, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x5F, 0x65, 0x6E, 0x64, 0x69, 0x61,
0x6E, 0x00, 0x78, 0x65, 0x5F, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x5F,
0x62, 0x61, 0x73, 0x65, 0x5F, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x00, 0x69,
0x6E, 0x74, 0x00, 0xAB, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2B, 0x06, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x70, 0x6F, 0x69, 0x6E, 0x74,
0x5F, 0x73, 0x69, 0x7A, 0x65, 0x00, 0x78, 0x65, 0x5F, 0x70, 0x6F, 0x69,
0x6E, 0x74, 0x5F, 0x73, 0x69, 0x7A, 0x65, 0x5F, 0x6D, 0x69, 0x6E, 0x5F,
0x6D, 0x61, 0x78, 0x00, 0x78, 0x65, 0x5F, 0x70, 0x6F, 0x69, 0x6E, 0x74,
0x5F, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6E, 0x5F, 0x74, 0x6F, 0x5F, 0x6E,
0x64, 0x63, 0x00, 0x78, 0x65, 0x5F, 0x75, 0x73, 0x65, 0x72, 0x5F, 0x63,
0x6C, 0x69, 0x70, 0x5F, 0x70, 0x6C, 0x61, 0x6E, 0x65, 0x73, 0x00, 0x66,
0x6C, 0x6F, 0x61, 0x74, 0x34, 0x00, 0xAB, 0xAB, 0x01, 0x00, 0x03, 0x00,
0x01, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xA3, 0x06, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x6E,
0x64, 0x63, 0x5F, 0x73, 0x63, 0x61, 0x6C, 0x65, 0x00, 0x66, 0x6C, 0x6F,
0x61, 0x74, 0x33, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x03, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xDD, 0x06, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x69, 0x6E, 0x74, 0x65, 0x72,
0x70, 0x6F, 0x6C, 0x61, 0x74, 0x6F, 0x72, 0x5F, 0x73, 0x61, 0x6D, 0x70,
0x6C, 0x69, 0x6E, 0x67, 0x5F, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6E,
0x00, 0x78, 0x65, 0x5F, 0x6E, 0x64, 0x63, 0x5F, 0x6F, 0x66, 0x66, 0x73,
0x65, 0x74, 0x00, 0x78, 0x65, 0x5F, 0x70, 0x73, 0x5F, 0x70, 0x61, 0x72,
0x61, 0x6D, 0x5F, 0x67, 0x65, 0x6E, 0x00, 0x78, 0x65, 0x5F, 0x74, 0x65,
0x78, 0x74, 0x75, 0x72, 0x65, 0x5F, 0x73, 0x77, 0x69, 0x7A, 0x7A, 0x6C,
0x65, 0x64, 0x5F, 0x73, 0x69, 0x67, 0x6E, 0x73, 0x00, 0x75, 0x69, 0x6E,
0x74, 0x34, 0x00, 0xAB, 0x01, 0x00, 0x13, 0x00, 0x01, 0x00, 0x04, 0x00,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x61, 0x07, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x74, 0x65, 0x78, 0x74, 0x75,
0x72, 0x65, 0x73, 0x5F, 0x72, 0x65, 0x73, 0x6F, 0x6C, 0x76, 0x65, 0x64,
0x00, 0x78, 0x65, 0x5F, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x5F, 0x63,
0x6F, 0x75, 0x6E, 0x74, 0x5F, 0x6C, 0x6F, 0x67, 0x32, 0x00, 0x75, 0x69,
0x6E, 0x74, 0x32, 0x00, 0x01, 0x00, 0x13, 0x00, 0x01, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xB6, 0x07, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x61, 0x6C, 0x70, 0x68, 0x61,
0x5F, 0x74, 0x65, 0x73, 0x74, 0x5F, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65,
0x6E, 0x63, 0x65, 0x00, 0x66, 0x6C, 0x6F, 0x61, 0x74, 0x00, 0xAB, 0xAB,
0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x07, 0x00, 0x00,
0x78, 0x65, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x5F, 0x65, 0x78, 0x70,
0x5F, 0x62, 0x69, 0x61, 0x73, 0x00, 0xAB, 0xAB, 0x01, 0x00, 0x03, 0x00,
0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xA3, 0x06, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x61,
0x6C, 0x70, 0x68, 0x61, 0x5F, 0x74, 0x6F, 0x5F, 0x6D, 0x61, 0x73, 0x6B,
0x00, 0x78, 0x65, 0x5F, 0x65, 0x64, 0x72, 0x61, 0x6D, 0x5F, 0x70, 0x69,
0x74, 0x63, 0x68, 0x5F, 0x74, 0x69, 0x6C, 0x65, 0x73, 0x00, 0x78, 0x65,
0x5F, 0x65, 0x64, 0x72, 0x61, 0x6D, 0x5F, 0x64, 0x65, 0x70, 0x74, 0x68,
0x5F, 0x72, 0x61, 0x6E, 0x67, 0x65, 0x00, 0x78, 0x65, 0x5F, 0x65, 0x64,
0x72, 0x61, 0x6D, 0x5F, 0x70, 0x6F, 0x6C, 0x79, 0x5F, 0x6F, 0x66, 0x66,
0x73, 0x65, 0x74, 0x5F, 0x66, 0x72, 0x6F, 0x6E, 0x74, 0x00, 0x78, 0x65,
0x5F, 0x65, 0x64, 0x72, 0x61, 0x6D, 0x5F, 0x70, 0x6F, 0x6C, 0x79, 0x5F,
0x6F, 0x66, 0x66, 0x73, 0x65, 0x74, 0x5F, 0x62, 0x61, 0x63, 0x6B, 0x00,
0x78, 0x65, 0x5F, 0x65, 0x64, 0x72, 0x61, 0x6D, 0x5F, 0x64, 0x65, 0x70,
0x74, 0x68, 0x5F, 0x62, 0x61, 0x73, 0x65, 0x5F, 0x64, 0x77, 0x6F, 0x72,
0x64, 0x73, 0x00, 0x78, 0x65, 0x5F, 0x65, 0x64, 0x72, 0x61, 0x6D, 0x5F,
0x73, 0x74, 0x65, 0x6E, 0x63, 0x69, 0x6C, 0x00, 0x01, 0x00, 0x13, 0x00,
0x01, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x61, 0x07, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x65,
0x64, 0x72, 0x61, 0x6D, 0x5F, 0x72, 0x74, 0x5F, 0x62, 0x61, 0x73, 0x65,
0x5F, 0x64, 0x77, 0x6F, 0x72, 0x64, 0x73, 0x5F, 0x73, 0x63, 0x61, 0x6C,
0x65, 0x64, 0x00, 0xAB, 0x01, 0x00, 0x13, 0x00, 0x01, 0x00, 0x04, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x61, 0x07, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x65, 0x64, 0x72, 0x61, 0x6D,
0x5F, 0x72, 0x74, 0x5F, 0x66, 0x6F, 0x72, 0x6D, 0x61, 0x74, 0x5F, 0x66,
0x6C, 0x61, 0x67, 0x73, 0x00, 0x78, 0x65, 0x5F, 0x65, 0x64, 0x72, 0x61,
0x6D, 0x5F, 0x72, 0x74, 0x5F, 0x63, 0x6C, 0x61, 0x6D, 0x70, 0x00, 0xAB,
0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA3, 0x06, 0x00, 0x00,
0x78, 0x65, 0x5F, 0x65, 0x64, 0x72, 0x61, 0x6D, 0x5F, 0x72, 0x74, 0x5F,
0x6B, 0x65, 0x65, 0x70, 0x5F, 0x6D, 0x61, 0x73, 0x6B, 0x00, 0xAB, 0xAB,
0x01, 0x00, 0x13, 0x00, 0x01, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x07, 0x00, 0x00,
0x78, 0x65, 0x5F, 0x65, 0x64, 0x72, 0x61, 0x6D, 0x5F, 0x72, 0x74, 0x5F,
0x62, 0x6C, 0x65, 0x6E, 0x64, 0x5F, 0x66, 0x61, 0x63, 0x74, 0x6F, 0x72,
0x73, 0x5F, 0x6F, 0x70, 0x73, 0x00, 0x78, 0x65, 0x5F, 0x65, 0x64, 0x72,
0x61, 0x6D, 0x5F, 0x62, 0x6C, 0x65, 0x6E, 0x64, 0x5F, 0x63, 0x6F, 0x6E,
0x73, 0x74, 0x61, 0x6E, 0x74, 0x00, 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73,
0x6F, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4C, 0x53, 0x4C,
0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6F, 0x6D, 0x70,
0x69, 0x6C, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2E, 0x31, 0x00, 0xAB, 0xAB,
0x49, 0x53, 0x47, 0x4E, 0x2C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x01, 0x00, 0x00, 0x58, 0x45, 0x56, 0x45, 0x52, 0x54, 0x45, 0x58,
0x49, 0x44, 0x00, 0xAB, 0x4F, 0x53, 0x47, 0x4E, 0x2C, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x0E, 0x00, 0x00, 0x58, 0x45, 0x56, 0x45,
0x52, 0x54, 0x45, 0x58, 0x49, 0x44, 0x00, 0xAB, 0x50, 0x43, 0x53, 0x47,
0xBC, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0E, 0x00, 0x00,
0x98, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x0E, 0x00, 0x00,
0x98, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x0E, 0x00, 0x00,
0x98, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x0E, 0x00, 0x00,
0xA6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x0E, 0x00, 0x00,
0xA6, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x0E, 0x00, 0x00,
0x53, 0x56, 0x5F, 0x54, 0x65, 0x73, 0x73, 0x46, 0x61, 0x63, 0x74, 0x6F,
0x72, 0x00, 0x53, 0x56, 0x5F, 0x49, 0x6E, 0x73, 0x69, 0x64, 0x65, 0x54,
0x65, 0x73, 0x73, 0x46, 0x61, 0x63, 0x74, 0x6F, 0x72, 0x00, 0xAB, 0xAB,
0x53, 0x48, 0x45, 0x58, 0x64, 0x01, 0x00, 0x00, 0x51, 0x00, 0x03, 0x00,
0x59, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x01, 0x93, 0x20, 0x00, 0x01,
0x94, 0x20, 0x00, 0x01, 0x95, 0x18, 0x00, 0x01, 0x96, 0x20, 0x00, 0x01,
0x97, 0x18, 0x00, 0x01, 0x6A, 0x08, 0x00, 0x01, 0x59, 0x00, 0x00, 0x07,
0x46, 0x8E, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x73, 0x00, 0x00, 0x01, 0x99, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x00,
0x5F, 0x00, 0x00, 0x02, 0x00, 0x70, 0x01, 0x00, 0x67, 0x00, 0x00, 0x04,
0x12, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x67, 0x00, 0x00, 0x04, 0x12, 0x20, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00,
0x0C, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x04, 0x12, 0x20, 0x10, 0x00,
0x02, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x04,
0x12, 0x20, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x68, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x04,
0x12, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x36, 0x00, 0x00, 0x04, 0x12, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0A, 0x70, 0x01, 0x00, 0x36, 0x00, 0x00, 0x08, 0x12, 0x20, 0x90, 0x00,
0x0A, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x80, 0x30, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x00, 0x01, 0x73, 0x00, 0x00, 0x01, 0x99, 0x00, 0x00, 0x02,
0x02, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x02, 0x00, 0x70, 0x01, 0x00,
0x67, 0x00, 0x00, 0x04, 0x12, 0x20, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x04, 0x12, 0x20, 0x10, 0x00,
0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x02,
0x01, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x04, 0x12, 0x20, 0x10, 0x00,
0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x04,
0x12, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x70, 0x01, 0x00,
0x36, 0x00, 0x00, 0x09, 0x12, 0x20, 0xD0, 0x00, 0x04, 0x00, 0x00, 0x00,
0x0A, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x80, 0x30, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x00, 0x01, 0x53, 0x54, 0x41, 0x54, 0x94, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
};

View File

@ -1,117 +0,0 @@
//
// Generated by Microsoft (R) HLSL Shader Compiler 10.1
//
//
// Buffer Definitions:
//
// cbuffer xe_system_cbuffer
// {
//
// uint xe_flags; // Offset: 0 Size: 4 [unused]
// float2 xe_tessellation_factor_range;// Offset: 4 Size: 8
// uint xe_line_loop_closing_index; // Offset: 12 Size: 4 [unused]
// uint xe_vertex_index_endian; // Offset: 16 Size: 4 [unused]
// int xe_vertex_base_index; // Offset: 20 Size: 4 [unused]
// float2 xe_point_size; // Offset: 24 Size: 8 [unused]
// float2 xe_point_size_min_max; // Offset: 32 Size: 8 [unused]
// float2 xe_point_screen_to_ndc; // Offset: 40 Size: 8 [unused]
// float4 xe_user_clip_planes[6]; // Offset: 48 Size: 96 [unused]
// float3 xe_ndc_scale; // Offset: 144 Size: 12 [unused]
// uint xe_interpolator_sampling_pattern;// Offset: 156 Size: 4 [unused]
// float3 xe_ndc_offset; // Offset: 160 Size: 12 [unused]
// uint xe_ps_param_gen; // Offset: 172 Size: 4 [unused]
// uint4 xe_texture_swizzled_signs[2];// Offset: 176 Size: 32 [unused]
// uint xe_textures_resolved; // Offset: 208 Size: 4 [unused]
// uint2 xe_sample_count_log2; // Offset: 212 Size: 8 [unused]
// float xe_alpha_test_reference; // Offset: 220 Size: 4 [unused]
// float4 xe_color_exp_bias; // Offset: 224 Size: 16 [unused]
// uint xe_alpha_to_mask; // Offset: 240 Size: 4 [unused]
// uint xe_edram_pitch_tiles; // Offset: 244 Size: 4 [unused]
// float2 xe_edram_depth_range; // Offset: 248 Size: 8 [unused]
// float2 xe_edram_poly_offset_front; // Offset: 256 Size: 8 [unused]
// float2 xe_edram_poly_offset_back; // Offset: 264 Size: 8 [unused]
// uint xe_edram_depth_base_dwords; // Offset: 272 Size: 4 [unused]
// uint4 xe_edram_stencil[2]; // Offset: 288 Size: 32 [unused]
// uint4 xe_edram_rt_base_dwords_scaled;// Offset: 320 Size: 16 [unused]
// uint4 xe_edram_rt_format_flags; // Offset: 336 Size: 16 [unused]
// float4 xe_edram_rt_clamp[4]; // Offset: 352 Size: 64 [unused]
// uint4 xe_edram_rt_keep_mask[2]; // Offset: 416 Size: 32 [unused]
// uint4 xe_edram_rt_blend_factors_ops;// Offset: 448 Size: 16 [unused]
// float4 xe_edram_blend_constant; // Offset: 464 Size: 16 [unused]
//
// }
//
//
// Resource Bindings:
//
// Name Type Format Dim ID HLSL Bind Count
// ------------------------------ ---------- ------- ----------- ------- -------------- ------
// xe_system_cbuffer cbuffer NA NA CB0 cb0 1
//
//
//
// Patch Constant signature:
//
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------- ------
// SV_TessFactor 0 x 0 QUADEDGE float x
// SV_TessFactor 1 x 1 QUADEDGE float x
// SV_TessFactor 2 x 2 QUADEDGE float x
// SV_TessFactor 3 x 3 QUADEDGE float x
// SV_InsideTessFactor 0 x 4 QUADINT float x
// SV_InsideTessFactor 1 x 5 QUADINT float x
//
//
// Input signature:
//
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------- ------
// XEVERTEXID 0 x 0 NONE float x
//
//
// Output signature:
//
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------- ------
// XEVERTEXID 0 x 0 NONE float x
//
// Tessellation Domain # of control points
// -------------------- --------------------
// Quadrilateral 4
//
// Tessellation Output Primitive Partitioning Type
// ------------------------------ ------------------
// Clockwise Triangles Even Fractional
//
hs_5_1
hs_decls
dcl_input_control_point_count 4
dcl_output_control_point_count 4
dcl_tessellator_domain domain_quad
dcl_tessellator_partitioning partitioning_fractional_even
dcl_tessellator_output_primitive output_triangle_cw
dcl_globalFlags refactoringAllowed
dcl_constantbuffer CB0[0:0][1], immediateIndexed, space=0
hs_fork_phase
dcl_hs_fork_phase_instance_count 4
dcl_input vForkInstanceID
dcl_output_siv o0.x, finalQuadUeq0EdgeTessFactor
dcl_output_siv o1.x, finalQuadVeq0EdgeTessFactor
dcl_output_siv o2.x, finalQuadUeq1EdgeTessFactor
dcl_output_siv o3.x, finalQuadVeq1EdgeTessFactor
dcl_temps 1
dcl_indexrange o0.x 4
mov r0.x, vForkInstanceID.x
mov o[r0.x + 0].x, CB0[0][0].z
ret
hs_fork_phase
dcl_hs_fork_phase_instance_count 2
dcl_input vForkInstanceID
dcl_output_siv o4.x, finalQuadUInsideTessFactor
dcl_output_siv o5.x, finalQuadVInsideTessFactor
dcl_temps 1
dcl_indexrange o4.x 2
mov r0.x, vForkInstanceID.x
mov o[r0.x + 4].x, CB0[0][0].z
ret
// Approximately 6 instruction slots used

Some files were not shown because too many files have changed in this diff Show More