Merge branch 'master' into linux_windowing
This commit is contained in:
commit
7edfdc2672
|
@ -8,6 +8,7 @@ skip_tags: true
|
|||
|
||||
skip_commits:
|
||||
files:
|
||||
- .drone.yml
|
||||
- .github/**
|
||||
- .travis.yml
|
||||
- docs/**
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
73
.travis.yml
73
.travis.yml
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -43,7 +43,6 @@ project("xenia-app")
|
|||
"mspack",
|
||||
"snappy",
|
||||
"spirv-tools",
|
||||
"volk",
|
||||
"xxhash",
|
||||
})
|
||||
defines({
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
namespace xe {
|
||||
namespace apu {
|
||||
|
||||
constexpr fourcc_t kAudioSaveSignature = make_fourcc("XAUD");
|
||||
|
||||
class AudioDriver;
|
||||
class XmaDecoder;
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 ®_info; \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<unsigned __int64>">
|
||||
<!-- Automatically convert endianness for xe::endian_store<,1> -->
|
||||
<Type Name="xe::endian_store<unsigned __int64, 1>">
|
||||
<DisplayString>
|
||||
{(((value & 0xFF00000000000000) >> 56) |
|
||||
((value & 0x00FF000000000000) >> 40) |
|
||||
|
@ -14,7 +14,7 @@
|
|||
((value & 0x00000000000000FF) << 56))}
|
||||
</DisplayString>
|
||||
</Type>
|
||||
<Type Name="xe::be<__int64>">
|
||||
<Type Name="xe::endian_store<__int64, 1>">
|
||||
<DisplayString>
|
||||
{(((value & 0xFF00000000000000) >> 56) |
|
||||
((value & 0x00FF000000000000) >> 40) |
|
||||
|
@ -27,7 +27,7 @@
|
|||
</DisplayString>
|
||||
</Type>
|
||||
|
||||
<Type Name="xe::be<unsigned int>">
|
||||
<Type Name="xe::endian_store<unsigned int, 1>">
|
||||
<DisplayString>
|
||||
{(((value & 0xFF000000) >> 24) |
|
||||
((value & 0x00FF0000) >> 8) |
|
||||
|
@ -35,7 +35,7 @@
|
|||
((value & 0x000000FF) << 24))}
|
||||
</DisplayString>
|
||||
</Type>
|
||||
<Type Name="xe::be<int>">
|
||||
<Type Name="xe::endian_store<int, 1>">
|
||||
<DisplayString>
|
||||
{(((value & 0xFF000000) >> 24) |
|
||||
((value & 0x00FF0000) >> 8) |
|
||||
|
@ -44,25 +44,25 @@
|
|||
</DisplayString>
|
||||
</Type>
|
||||
|
||||
<Type Name="xe::be<unsigned short>">
|
||||
<Type Name="xe::endian_store<unsigned short, 1>">
|
||||
<DisplayString>
|
||||
{(((value & 0xFF00) >> 8) |
|
||||
((value & 0x00FF) << 8))}
|
||||
</DisplayString>
|
||||
</Type>
|
||||
<Type Name="xe::be<short>">
|
||||
<Type Name="xe::endian_store<short, 1>">
|
||||
<DisplayString>
|
||||
{(((value & 0xFF00) >> 8) |
|
||||
((value & 0x00FF) << 8))}
|
||||
</DisplayString>
|
||||
</Type>
|
||||
|
||||
<Type Name="xe::be<unsigned char>">
|
||||
<Type Name="xe::endian_store<unsigned char, 1>">
|
||||
<DisplayString>
|
||||
{value}
|
||||
</DisplayString>
|
||||
</Type>
|
||||
<Type Name="xe::be<char>">
|
||||
<Type Name="xe::endian_store<char, 1>">
|
||||
<DisplayString>
|
||||
{value}
|
||||
</DisplayString>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 = {};
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ struct PPCBuiltins {
|
|||
Function* check_global_lock;
|
||||
Function* enter_global_lock;
|
||||
Function* leave_global_lock;
|
||||
Function* syscall_handler;
|
||||
};
|
||||
|
||||
class PPCFrontend {
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -34,6 +34,8 @@ DECLARE_bool(debug);
|
|||
namespace xe {
|
||||
namespace cpu {
|
||||
|
||||
constexpr fourcc_t kProcessorSaveSignature = make_fourcc("PROC");
|
||||
|
||||
class Breakpoint;
|
||||
class StackWalker;
|
||||
class XexModule;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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_);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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_ = "";
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -16,7 +16,7 @@ project("xenia-gpu-d3d12")
|
|||
})
|
||||
local_platform_files()
|
||||
files({
|
||||
"shaders/bin/*.h",
|
||||
"../shaders/bytecode/d3d12_5_1/*.h",
|
||||
})
|
||||
|
||||
group("src")
|
||||
|
|
|
@ -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
|
|
@ -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_
|
Binary file not shown.
|
@ -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,
|
||||
};
|
|
@ -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
|
Binary file not shown.
|
@ -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,
|
||||
};
|
|
@ -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
|
Binary file not shown.
|
@ -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,
|
||||
};
|
|
@ -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
|
Binary file not shown.
|
@ -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,
|
||||
};
|
|
@ -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
Loading…
Reference in New Issue