Compare commits

..

23 Commits

Author SHA1 Message Date
bunnei
dc29919bbe Revert "arm_dynarmic: Check if jit is nullptr when preparing reschedule" 2019-09-29 21:54:19 -04:00
bunnei
28538bba9c Merge pull request #2574 from DarkLordZach/dynarmic-jit-nullptr
arm_dynarmic: Check if jit is nullptr when preparing reschedule
2019-09-29 21:44:10 -04:00
Rodrigo Locatti
8a2e96de2e Merge pull request #2909 from MysticExile/fmt-2
externals: Update fmt to master
2019-09-27 14:09:32 -03:00
Rodrigo Locatti
c4a99944f2 Merge pull request #2911 from DarkLordZach/azure-patreon
ci: Add azure patreon pipeline
2019-09-26 19:30:33 -03:00
Zach Hilman
867e1db287 ci: Add two step patreon build pipeline 2019-09-26 09:42:22 -04:00
Zach Hilman
0ba7055873 ci: Add private mergebot CI templates 2019-09-26 09:29:33 -04:00
Zach Hilman
3f52bb5677 ci: Optionally prepend commit messages with environment variable 2019-09-26 09:29:15 -04:00
Zach Hilman
ab8d122384 ci: Add private mergebot script
Uses Azure instead of GitHub
2019-09-26 09:28:59 -04:00
Ethan
3e5790de79 externals: Update fmt to master 2019-09-26 00:34:37 +02:00
bunnei
da07373599 Merge pull request #2864 from ReinUsesLisp/fix-clang-decompiler
gl_shader_decompiler: Fix clang build issues
2019-09-25 17:46:02 -04:00
ReinUsesLisp
f926230ab1 gl_shader_decompiler: Add tailing return for HUnpack2 2019-09-24 01:03:59 -03:00
ReinUsesLisp
25bfaffdff gl_shader_decompiler: Fix clang build issues 2019-09-24 01:03:27 -03:00
bunnei
376f1a4432 Merge pull request #2869 from ReinUsesLisp/suld
shader/image: Implement SULD and fix SUATOM
2019-09-23 21:47:03 -04:00
James Rowe
2f5545f8de Merge pull request #2901 from DarkLordZach/mainline-title-bar
ci: Properly parse Azure CI details in title bar
2019-09-22 18:14:08 -06:00
Zach Hilman
921dae5e83 ci: Correct GitHub Release name 2019-09-22 19:06:48 -04:00
Zach Hilman
15805f4e01 ci: Determine build date via bash 2019-09-22 18:39:29 -04:00
Zach Hilman
851c5d67ae ci: Update to use date as build number 2019-09-22 17:23:47 -04:00
Zach Hilman
14248685af cmake: Add SCM detection for Azure 2019-09-22 17:23:10 -04:00
Zach Hilman
cc3db2aa43 ci: Split mainline pipeline and add support for GitHub releases (#2900)
* ci: Add mock build alternative for fast testing

* ci: Always cache build

* ci: Extract steps to download build stage artifacts

* ci: Add template to release to GitHub

* ci: Add template to release to Azure Universal Artifacts

* ci: Split mainline to two pipelines
2019-09-22 20:01:29 +00:00
ReinUsesLisp
44000971e2 gl_shader_decompiler: Use uint for images and fix SUATOM
In the process remove implementation of SUATOM.MIN and SUATOM.MAX as
these require a distinction between U32 and S32. These have to be
implemented with imageCompSwap loop.
2019-09-21 17:33:52 -03:00
ReinUsesLisp
675f23aedc shader/image: Implement SULD and remove irrelevant code
* Implement SULD as float.
* Remove conditional declaration of GL_ARB_shader_viewport_layer_array.
2019-09-21 17:32:48 -03:00
ReinUsesLisp
4de0f1e1c8 shader_bytecode: Add SULD encoding 2019-09-21 17:31:46 -03:00
Zach Hilman
425cdf946c arm_dynarmic: Check if jit is nullptr when preparing reschedule
Prevents crash with multiprocess loading.
2019-06-10 00:14:25 -04:00
21 changed files with 371 additions and 267 deletions

View File

@@ -0,0 +1,41 @@
# Download all pull requests as patches that match a specific label
# Usage: python download-patches-by-label.py <Label to Match> <Root Path Folder to DL to>
import requests, sys, json, urllib3.request, shutil, subprocess, os, traceback
org = os.getenv("PrivateMergeOrg".upper(), "yuzu-emu")
repo = os.getenv("PrivateMergeRepo".upper(), "yuzu-private")
tagline = os.getenv("MergeTaglinePrivate".upper(), "")
user = sys.argv[1]
http = urllib3.PoolManager()
dl_list = {}
def check_individual(repo_id, pr_id):
url = 'https://%sdev.azure.com/%s/%s/_apis/git/repositories/%s/pullRequests/%s/labels?api-version=5.1-preview.1' % (user, org, repo, repo_id, pr_id)
response = requests.get(url)
if (response.ok):
j = json.loads(response.content)
for tg in j['value']:
if (tg['name'] == sys.argv[2]):
return True
return False
try:
url = 'https://%sdev.azure.com/%s/%s/_apis/git/pullrequests?api-version=5.1' % (user, org, repo)
response = requests.get(url)
if (response.ok):
j = json.loads(response.content)
for pr in j["value"]:
repo_id = pr['repository']['id']
pr_id = pr['pullRequestId']
if (check_individual(repo_id, pr_id)):
pn = pr_id
ref = pr['sourceRefName']
print("Matched PR# %s" % pn)
print(subprocess.check_output(["git", "fetch", "https://%sdev.azure.com/%s/_git/%s" % (user, org, repo), ref, "-f"]))
print(subprocess.check_output(["git", "merge", "--squash", 'origin/' + ref.replace('refs/heads/','')]))
print(subprocess.check_output(["git", "commit", "-m\"Merge %s PR %s\"" % (tagline, pn)]))
except:
traceback.print_exc(file=sys.stdout)
sys.exit(-1)

View File

@@ -1,7 +1,9 @@
# Download all pull requests as patches that match a specific label
# Usage: python download-patches-by-label.py <Label to Match> <Root Path Folder to DL to>
import requests, sys, json, urllib3.request, shutil, subprocess
import requests, sys, json, urllib3.request, shutil, subprocess, os
tagline = os.getenv("MergeTaglinePublic".upper(), "")
http = urllib3.PoolManager()
dl_list = {}
@@ -23,6 +25,6 @@ try:
print("Matched PR# %s" % pn)
print(subprocess.check_output(["git", "fetch", "https://github.com/yuzu-emu/yuzu.git", "pull/%s/head:pr-%s" % (pn, pn), "-f"]))
print(subprocess.check_output(["git", "merge", "--squash", "pr-%s" % pn]))
print(subprocess.check_output(["git", "commit", "-m\"Merge PR %s\"" % pn]))
print(subprocess.check_output(["git", "commit", "-m\"Merge %s PR %s\"" % (tagline, pn)]))
except:
sys.exit(-1)

View File

@@ -3,6 +3,8 @@ parameters:
cache: 'false'
steps:
- script: export DATE=`date '+%Y.%m.%d'` && export CI=true && AZURE_REPO_NAME=yuzu-emu/yuzu-$(BuildName) && AZURE_REPO_TAG=$(BuildName)-$DATE
displayName: 'Determine Build Name'
- task: DockerInstaller@0
displayName: 'Prepare Environment'
inputs:

View File

@@ -0,0 +1,47 @@
jobs:
- job: merge
displayName: 'pull requests'
steps:
- checkout: self
submodules: recursive
- template: ./mergebot-private.yml
parameters:
matchLabel: '$(BuildName)-merge'
matchLabelPublic: '$(PublicBuildName)-merge'
- task: ArchiveFiles@2
displayName: 'Package Source'
inputs:
rootFolderOrFile: '$(System.DefaultWorkingDirectory)'
includeRootFolder: false
archiveType: '7z'
archiveFile: '$(Build.ArtifactStagingDirectory)/yuzu-$(BuildName)-source.7z'
- task: PublishPipelineArtifact@1
displayName: 'Upload Artifacts'
inputs:
targetPath: '$(Build.ArtifactStagingDirectory)/yuzu-$(BuildName)-source.7z'
artifact: 'yuzu-$(BuildName)-source'
replaceExistingArchive: true
- job: upload_source
displayName: 'upload'
dependsOn: merge
steps:
- template: ./sync-source.yml
parameters:
artifactSource: 'true'
needSubmodules: 'true'
- script: chmod a+x $(System.DefaultWorkingDirectory)/.ci/scripts/merge/yuzubot-git-config.sh && $(System.DefaultWorkingDirectory)/.ci/scripts/merge/yuzubot-git-config.sh
displayName: 'Apply Git Configuration'
- script: git tag -a $(BuildName)-$(Build.BuildId) -m "yuzu $(BuildName) $(Build.BuildNumber) $(Build.DefinitionName)"
displayName: 'Tag Source'
- script: git remote add other $(GitRepoPushChangesURL)
displayName: 'Register Repository'
- script: git push --follow-tags --force other HEAD:$(GitPushBranch)
displayName: 'Update Code'
- script: git rev-list -n 1 $(BuildName)-$(Build.BuildId) > $(Build.ArtifactStagingDirectory)/tag-commit.sha
displayName: 'Calculate Release Point'
- task: PublishPipelineArtifact@1
displayName: 'Upload Release Point'
inputs:
targetPath: '$(Build.ArtifactStagingDirectory)/tag-commit.sha'
artifact: 'yuzu-$(BuildName)-release-point'
replaceExistingArchive: true

View File

@@ -0,0 +1,23 @@
parameters:
matchLabel: 'dummy-merge'
matchLabelPublic: 'dummy-merge'
steps:
- script: mkdir $(System.DefaultWorkingDirectory)/patches && pip install requests urllib3
displayName: 'Prepare Environment'
- script: chmod a+x $(System.DefaultWorkingDirectory)/.ci/scripts/merge/yuzubot-git-config.sh && $(System.DefaultWorkingDirectory)/.ci/scripts/merge/yuzubot-git-config.sh
displayName: 'Apply Git Configuration'
- task: PythonScript@0
displayName: 'Discover, Download, and Apply Patches'
inputs:
scriptSource: 'filePath'
scriptPath: '.ci/scripts/merge/apply-patches-by-label.py'
arguments: '${{ parameters.matchLabelPublic }} patches-public'
workingDirectory: '$(System.DefaultWorkingDirectory)'
- task: PythonScript@0
displayName: 'Discover, Download, and Apply Patches'
inputs:
scriptSource: 'filePath'
scriptPath: '.ci/scripts/merge/apply-patches-by-label-private.py'
arguments: '$(PrivateMergeUser) ${{ parameters.matchLabel }} patches-private'
workingDirectory: '$(System.DefaultWorkingDirectory)'

View File

@@ -0,0 +1,8 @@
trigger:
- master
stages:
- stage: merge
displayName: 'merge'
jobs:
- template: ./templates/merge-private.yml

View File

@@ -0,0 +1,28 @@
trigger:
- master
stages:
- stage: format
displayName: 'format'
jobs:
- job: format
displayName: 'clang'
pool:
vmImage: ubuntu-latest
steps:
- template: ./templates/format-check.yml
- stage: build
dependsOn: format
displayName: 'build'
jobs:
- template: ./templates/build-standard.yml
parameters:
cache: 'true'
- stage: release
displayName: 'release'
dependsOn: build
jobs:
- job: azure
displayName: 'azure'
steps:
- template: ./templates/release-universal.yml

View File

@@ -1,19 +0,0 @@
# Starter pipeline
# Start with a minimal pipeline that you can customize to build and deploy your code.
# Add steps that build, run tests, deploy, and more:
# https://aka.ms/yaml
trigger:
- master
pool:
vmImage: 'ubuntu-latest'
steps:
- script: echo Hello, world!
displayName: 'Run a one-line script'
- script: |
echo Add other tasks to build, test, and deploy your project.
echo See https://aka.ms/yaml
displayName: 'Run a multi-line script'

2
externals/fmt vendored

View File

@@ -10,6 +10,9 @@ if (DEFINED ENV{CI})
elseif(DEFINED ENV{APPVEYOR})
set(BUILD_REPOSITORY $ENV{APPVEYOR_REPO_NAME})
set(BUILD_TAG $ENV{APPVEYOR_REPO_TAG_NAME})
elseif(DEFINED ENV{AZURE})
set(BUILD_REPOSITORY $ENV{AZURE_REPO_NAME})
set(BUILD_TAG $ENV{AZURE_REPO_TAG})
endif()
endif()
add_custom_command(OUTPUT scm_rev.cpp

View File

@@ -544,7 +544,7 @@ enum class VoteOperation : u64 {
Eq = 2, // allThreadsEqualNV
};
enum class ImageAtomicSize : u64 {
enum class ImageAtomicOperationType : u64 {
U32 = 0,
S32 = 1,
U64 = 2,
@@ -1432,11 +1432,11 @@ union Instruction {
ASSERT(mode == SurfaceDataMode::D_BA);
return store_data_layout;
}
} sust;
} suldst;
union {
BitField<28, 1, u64> is_ba;
BitField<51, 3, ImageAtomicSize> size;
BitField<51, 3, ImageAtomicOperationType> operation_type;
BitField<33, 3, ImageType> image_type;
BitField<29, 4, ImageAtomicOperation> operation;
BitField<49, 2, OutOfBoundsStore> out_of_bounds_store;
@@ -1595,6 +1595,7 @@ public:
TMML_B, // Texture Mip Map Level
TMML, // Texture Mip Map Level
SUST, // Surface Store
SULD, // Surface Load
SUATOM, // Surface Atomic Operation
EXIT,
NOP,
@@ -1884,6 +1885,7 @@ private:
INST("110111110110----", Id::TMML_B, Type::Texture, "TMML_B"),
INST("1101111101011---", Id::TMML, Type::Texture, "TMML"),
INST("11101011001-----", Id::SUST, Type::Image, "SUST"),
INST("11101011000-----", Id::SULD, Type::Image, "SULD"),
INST("1110101000------", Id::SUATOM, Type::Image, "SUATOM_D"),
INST("0101000010110---", Id::NOP, Type::Trivial, "NOP"),
INST("11100000--------", Id::IPA, Type::Trivial, "IPA"),

View File

@@ -2,8 +2,10 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <array>
#include <cstddef>
#include <vector>
#include <glad/glad.h>
#include "common/logging/log.h"
@@ -30,9 +32,27 @@ bool TestProgram(const GLchar* glsl) {
return link_status == GL_TRUE;
}
std::vector<std::string_view> GetExtensions() {
GLint num_extensions;
glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions);
std::vector<std::string_view> extensions;
extensions.reserve(num_extensions);
for (GLint index = 0; index < num_extensions; ++index) {
extensions.push_back(
reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, static_cast<GLuint>(index))));
}
return extensions;
}
bool HasExtension(const std::vector<std::string_view>& images, std::string_view extension) {
return std::find(images.begin(), images.end(), extension) != images.end();
}
} // Anonymous namespace
Device::Device() {
const std::vector extensions = GetExtensions();
uniform_buffer_alignment = GetInteger<std::size_t>(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT);
shader_storage_alignment = GetInteger<std::size_t>(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT);
max_vertex_attributes = GetInteger<u32>(GL_MAX_VERTEX_ATTRIBS);
@@ -40,6 +60,7 @@ Device::Device() {
has_warp_intrinsics = GLAD_GL_NV_gpu_shader5 && GLAD_GL_NV_shader_thread_group &&
GLAD_GL_NV_shader_thread_shuffle;
has_vertex_viewport_layer = GLAD_GL_ARB_shader_viewport_layer_array;
has_image_load_formatted = HasExtension(extensions, "GL_EXT_shader_image_load_formatted");
has_variable_aoffi = TestVariableAoffi();
has_component_indexing_bug = TestComponentIndexingBug();
has_precise_bug = TestPreciseBug();
@@ -55,6 +76,7 @@ Device::Device(std::nullptr_t) {
max_varyings = 15;
has_warp_intrinsics = true;
has_vertex_viewport_layer = true;
has_image_load_formatted = true;
has_variable_aoffi = true;
has_component_indexing_bug = false;
has_precise_bug = false;

View File

@@ -38,6 +38,10 @@ public:
return has_vertex_viewport_layer;
}
bool HasImageLoadFormatted() const {
return has_image_load_formatted;
}
bool HasVariableAoffi() const {
return has_variable_aoffi;
}
@@ -61,6 +65,7 @@ private:
u32 max_varyings{};
bool has_warp_intrinsics{};
bool has_vertex_viewport_layer{};
bool has_image_load_formatted{};
bool has_variable_aoffi{};
bool has_component_indexing_bug{};
bool has_precise_bug{};

View File

@@ -211,14 +211,14 @@ CachedProgram SpecializeShader(const std::string& code, const GLShader::ShaderEn
const auto primitive_mode{variant.primitive_mode};
const auto texture_buffer_usage{variant.texture_buffer_usage};
std::string source = "#version 430 core\n"
"#extension GL_ARB_separate_shader_objects : enable\n"
"#extension GL_NV_gpu_shader5 : enable\n"
"#extension GL_NV_shader_thread_group : enable\n"
"#extension GL_NV_shader_thread_shuffle : enable\n";
if (entries.shader_viewport_layer_array) {
source += "#extension GL_ARB_shader_viewport_layer_array : enable\n";
}
std::string source = R"(#version 430 core
#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_shader_viewport_layer_array : enable
#extension GL_EXT_shader_image_load_formatted : enable
#extension GL_NV_gpu_shader5 : enable
#extension GL_NV_shader_thread_group : enable
#extension GL_NV_shader_thread_shuffle : enable
)";
if (program_type == ProgramType::Compute) {
source += "#extension GL_ARB_compute_variable_group_size : require\n";
}

View File

@@ -19,6 +19,7 @@
#include "video_core/renderer_opengl/gl_device.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
#include "video_core/shader/node.h"
#include "video_core/shader/shader_ir.h"
namespace OpenGL::GLShader {
@@ -241,6 +242,26 @@ constexpr const char* GetTypeString(Type type) {
}
}
constexpr const char* GetImageTypeDeclaration(Tegra::Shader::ImageType image_type) {
switch (image_type) {
case Tegra::Shader::ImageType::Texture1D:
return "1D";
case Tegra::Shader::ImageType::TextureBuffer:
return "Buffer";
case Tegra::Shader::ImageType::Texture1DArray:
return "1DArray";
case Tegra::Shader::ImageType::Texture2D:
return "2D";
case Tegra::Shader::ImageType::Texture2DArray:
return "2DArray";
case Tegra::Shader::ImageType::Texture3D:
return "3D";
default:
UNREACHABLE();
return "1D";
}
}
/// Generates code to use for a swizzle operation.
constexpr const char* GetSwizzle(u32 element) {
constexpr std::array swizzle = {".x", ".y", ".z", ".w"};
@@ -398,8 +419,6 @@ public:
usage.is_read, usage.is_written);
}
entries.clip_distances = ir.GetClipDistances();
entries.shader_viewport_layer_array =
IsVertexShader(stage) && (ir.UsesLayer() || ir.UsesViewportIndex());
entries.shader_length = ir.GetLength();
return entries;
}
@@ -722,42 +741,6 @@ private:
void DeclareImages() {
const auto& images{ir.GetImages()};
for (const auto& [offset, image] : images) {
const char* image_type = [&] {
switch (image.GetType()) {
case Tegra::Shader::ImageType::Texture1D:
return "image1D";
case Tegra::Shader::ImageType::TextureBuffer:
return "imageBuffer";
case Tegra::Shader::ImageType::Texture1DArray:
return "image1DArray";
case Tegra::Shader::ImageType::Texture2D:
return "image2D";
case Tegra::Shader::ImageType::Texture2DArray:
return "image2DArray";
case Tegra::Shader::ImageType::Texture3D:
return "image3D";
default:
UNREACHABLE();
return "image1D";
}
}();
const auto [type_prefix, format] = [&]() -> std::pair<const char*, const char*> {
if (!image.IsSizeKnown()) {
return {"", ""};
}
switch (image.GetSize()) {
case Tegra::Shader::ImageAtomicSize::U32:
return {"u", "r32ui, "};
case Tegra::Shader::ImageAtomicSize::S32:
return {"i", "r32i, "};
default:
UNIMPLEMENTED_MSG("Unimplemented atomic size={}",
static_cast<u32>(image.GetSize()));
return {"", ""};
}
}();
std::string qualifier = "coherent volatile";
if (image.IsRead() && !image.IsWritten()) {
qualifier += " readonly";
@@ -765,9 +748,10 @@ private:
qualifier += " writeonly";
}
code.AddLine("layout (binding = IMAGE_BINDING_{}) {} uniform "
"{} {};",
image.GetIndex(), qualifier, image_type, GetImage(image));
const char* format = image.IsAtomic() ? "r32ui, " : "";
const char* type_declaration = GetImageTypeDeclaration(image.GetType());
code.AddLine("layout ({}binding = IMAGE_BINDING_{}) {} uniform uimage{} {};", format,
image.GetIndex(), qualifier, type_declaration, GetImage(image));
}
if (!images.empty()) {
code.AddNewLine();
@@ -1234,28 +1218,13 @@ private:
}
std::string BuildImageValues(Operation operation) {
constexpr std::array constructors{"uint", "uvec2", "uvec3", "uvec4"};
const auto meta{std::get<MetaImage>(operation.GetMeta())};
const auto [constructors, type] = [&]() -> std::pair<std::array<const char*, 4>, Type> {
constexpr std::array float_constructors{"float", "vec2", "vec3", "vec4"};
if (!meta.image.IsSizeKnown()) {
return {float_constructors, Type::Float};
}
switch (meta.image.GetSize()) {
case Tegra::Shader::ImageAtomicSize::U32:
return {{"uint", "uvec2", "uvec3", "uvec4"}, Type::Uint};
case Tegra::Shader::ImageAtomicSize::S32:
return {{"int", "ivec2", "ivec3", "ivec4"}, Type::Uint};
default:
UNIMPLEMENTED_MSG("Unimplemented image size={}",
static_cast<u32>(meta.image.GetSize()));
return {float_constructors, Type::Float};
}
}();
const std::size_t values_count{meta.values.size()};
std::string expr = fmt::format("{}(", constructors.at(values_count - 1));
for (std::size_t i = 0; i < values_count; ++i) {
expr += Visit(meta.values.at(i)).As(type);
expr += Visit(meta.values.at(i)).AsUint();
if (i + 1 < values_count) {
expr += ", ";
}
@@ -1264,29 +1233,6 @@ private:
return expr;
}
Expression AtomicImage(Operation operation, const char* opname) {
constexpr std::array constructors{"int(", "ivec2(", "ivec3(", "ivec4("};
const auto meta{std::get<MetaImage>(operation.GetMeta())};
ASSERT(meta.values.size() == 1);
ASSERT(meta.image.IsSizeKnown());
const auto type = [&]() {
switch (const auto size = meta.image.GetSize()) {
case Tegra::Shader::ImageAtomicSize::U32:
return Type::Uint;
case Tegra::Shader::ImageAtomicSize::S32:
return Type::Int;
default:
UNIMPLEMENTED_MSG("Unimplemented image size={}", static_cast<u32>(size));
return Type::Uint;
}
}();
return {fmt::format("{}({}, {}, {})", opname, GetImage(meta.image),
BuildIntegerCoordinates(operation), Visit(meta.values[0]).As(type)),
type};
}
Expression Assign(Operation operation) {
const Node& dest = operation[0];
const Node& src = operation[1];
@@ -1545,6 +1491,8 @@ private:
case Tegra::Shader::HalfType::H1_H1:
return {fmt::format("vec2({}[1])", operand.AsHalfFloat()), Type::HalfFloat};
}
UNREACHABLE();
return {"0", Type::Int};
}
Expression HMergeF32(Operation operation) {
@@ -1809,6 +1757,19 @@ private:
return {tmp, Type::Float};
}
Expression ImageLoad(Operation operation) {
if (!device.HasImageLoadFormatted()) {
LOG_ERROR(Render_OpenGL,
"Device lacks GL_EXT_shader_image_load_formatted, stubbing image load");
return {"0", Type::Int};
}
const auto meta{std::get<MetaImage>(operation.GetMeta())};
return {fmt::format("imageLoad({}, {}){}", GetImage(meta.image),
BuildIntegerCoordinates(operation), GetSwizzle(meta.element)),
Type::Uint};
}
Expression ImageStore(Operation operation) {
const auto meta{std::get<MetaImage>(operation.GetMeta())};
code.AddLine("imageStore({}, {}, {});", GetImage(meta.image),
@@ -1816,31 +1777,14 @@ private:
return {};
}
Expression AtomicImageAdd(Operation operation) {
return AtomicImage(operation, "imageAtomicAdd");
}
template <const std::string_view& opname>
Expression AtomicImage(Operation operation) {
const auto meta{std::get<MetaImage>(operation.GetMeta())};
ASSERT(meta.values.size() == 1);
Expression AtomicImageMin(Operation operation) {
return AtomicImage(operation, "imageAtomicMin");
}
Expression AtomicImageMax(Operation operation) {
return AtomicImage(operation, "imageAtomicMax");
}
Expression AtomicImageAnd(Operation operation) {
return AtomicImage(operation, "imageAtomicAnd");
}
Expression AtomicImageOr(Operation operation) {
return AtomicImage(operation, "imageAtomicOr");
}
Expression AtomicImageXor(Operation operation) {
return AtomicImage(operation, "imageAtomicXor");
}
Expression AtomicImageExchange(Operation operation) {
return AtomicImage(operation, "imageAtomicExchange");
return {fmt::format("imageAtomic{}({}, {}, {})", opname, GetImage(meta.image),
BuildIntegerCoordinates(operation), Visit(meta.values[0]).AsUint()),
Type::Uint};
}
Expression Branch(Operation operation) {
@@ -2035,6 +1979,12 @@ private:
Func() = delete;
~Func() = delete;
static constexpr std::string_view Add = "Add";
static constexpr std::string_view And = "And";
static constexpr std::string_view Or = "Or";
static constexpr std::string_view Xor = "Xor";
static constexpr std::string_view Exchange = "Exchange";
static constexpr std::string_view ShuffleIndexed = "shuffleNV";
static constexpr std::string_view ShuffleUp = "shuffleUpNV";
static constexpr std::string_view ShuffleDown = "shuffleDownNV";
@@ -2172,14 +2122,14 @@ private:
&GLSLDecompiler::TextureQueryLod,
&GLSLDecompiler::TexelFetch,
&GLSLDecompiler::ImageLoad,
&GLSLDecompiler::ImageStore,
&GLSLDecompiler::AtomicImageAdd,
&GLSLDecompiler::AtomicImageMin,
&GLSLDecompiler::AtomicImageMax,
&GLSLDecompiler::AtomicImageAnd,
&GLSLDecompiler::AtomicImageOr,
&GLSLDecompiler::AtomicImageXor,
&GLSLDecompiler::AtomicImageExchange,
&GLSLDecompiler::AtomicImage<Func::Add>,
&GLSLDecompiler::AtomicImage<Func::And>,
&GLSLDecompiler::AtomicImage<Func::Or>,
&GLSLDecompiler::AtomicImage<Func::Xor>,
&GLSLDecompiler::AtomicImage<Func::Exchange>,
&GLSLDecompiler::Branch,
&GLSLDecompiler::BranchIndirect,

View File

@@ -90,7 +90,6 @@ struct ShaderEntries {
std::vector<ImageEntry> images;
std::vector<GlobalMemoryEntry> global_memory_entries;
std::array<bool, Maxwell::NumClipDistances> clip_distances{};
bool shader_viewport_layer_array{};
std::size_t shader_length{};
};

View File

@@ -343,20 +343,17 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn
u8 is_bindless{};
u8 is_written{};
u8 is_read{};
u8 is_size_known{};
u32 size{};
u8 is_atomic{};
if (!LoadObjectFromPrecompiled(offset) || !LoadObjectFromPrecompiled(index) ||
!LoadObjectFromPrecompiled(type) || !LoadObjectFromPrecompiled(is_bindless) ||
!LoadObjectFromPrecompiled(is_written) || !LoadObjectFromPrecompiled(is_read) ||
!LoadObjectFromPrecompiled(is_size_known) || !LoadObjectFromPrecompiled(size)) {
!LoadObjectFromPrecompiled(is_atomic)) {
return {};
}
entry.entries.images.emplace_back(
static_cast<std::size_t>(offset), static_cast<std::size_t>(index),
static_cast<Tegra::Shader::ImageType>(type), is_bindless != 0, is_written != 0,
is_read != 0,
is_size_known ? std::make_optional(static_cast<Tegra::Shader::ImageAtomicSize>(size))
: std::nullopt);
is_read != 0, is_atomic != 0);
}
u32 global_memory_count{};
@@ -382,12 +379,6 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn
}
}
bool shader_viewport_layer_array{};
if (!LoadObjectFromPrecompiled(shader_viewport_layer_array)) {
return {};
}
entry.entries.shader_viewport_layer_array = shader_viewport_layer_array;
u64 shader_length{};
if (!LoadObjectFromPrecompiled(shader_length)) {
return {};
@@ -435,14 +426,13 @@ bool ShaderDiskCacheOpenGL::SaveDecompiledFile(u64 unique_identifier, const std:
return false;
}
for (const auto& image : entries.images) {
const u32 size = image.IsSizeKnown() ? static_cast<u32>(image.GetSize()) : 0U;
if (!SaveObjectToPrecompiled(static_cast<u64>(image.GetOffset())) ||
!SaveObjectToPrecompiled(static_cast<u64>(image.GetIndex())) ||
!SaveObjectToPrecompiled(static_cast<u32>(image.GetType())) ||
!SaveObjectToPrecompiled(static_cast<u8>(image.IsBindless() ? 1 : 0)) ||
!SaveObjectToPrecompiled(static_cast<u8>(image.IsWritten() ? 1 : 0)) ||
!SaveObjectToPrecompiled(static_cast<u8>(image.IsRead() ? 1 : 0)) ||
!SaveObjectToPrecompiled(image.IsSizeKnown()) || !SaveObjectToPrecompiled(size)) {
!SaveObjectToPrecompiled(static_cast<u8>(image.IsAtomic() ? 1 : 0))) {
return false;
}
}
@@ -464,10 +454,6 @@ bool ShaderDiskCacheOpenGL::SaveDecompiledFile(u64 unique_identifier, const std:
}
}
if (!SaveObjectToPrecompiled(entries.shader_viewport_layer_array)) {
return false;
}
if (!SaveObjectToPrecompiled(static_cast<u64>(entries.shader_length))) {
return false;
}

View File

@@ -19,6 +19,7 @@
#include "video_core/engines/shader_header.h"
#include "video_core/renderer_vulkan/vk_device.h"
#include "video_core/renderer_vulkan/vk_shader_decompiler.h"
#include "video_core/shader/node.h"
#include "video_core/shader/shader_ir.h"
namespace Vulkan::VKShader {
@@ -939,6 +940,11 @@ private:
return {};
}
Id ImageLoad(Operation operation) {
UNIMPLEMENTED();
return {};
}
Id ImageStore(Operation operation) {
UNIMPLEMENTED();
return {};
@@ -949,16 +955,6 @@ private:
return {};
}
Id AtomicImageMin(Operation operation) {
UNIMPLEMENTED();
return {};
}
Id AtomicImageMax(Operation operation) {
UNIMPLEMENTED();
return {};
}
Id AtomicImageAnd(Operation operation) {
UNIMPLEMENTED();
return {};
@@ -1440,10 +1436,9 @@ private:
&SPIRVDecompiler::TextureQueryLod,
&SPIRVDecompiler::TexelFetch,
&SPIRVDecompiler::ImageLoad,
&SPIRVDecompiler::ImageStore,
&SPIRVDecompiler::AtomicImageAdd,
&SPIRVDecompiler::AtomicImageMin,
&SPIRVDecompiler::AtomicImageMax,
&SPIRVDecompiler::AtomicImageAnd,
&SPIRVDecompiler::AtomicImageOr,
&SPIRVDecompiler::AtomicImageXor,

View File

@@ -41,11 +41,46 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) {
const Instruction instr = {program_code[pc]};
const auto opcode = OpCode::Decode(instr);
const auto GetCoordinates = [this, instr](Tegra::Shader::ImageType image_type) {
std::vector<Node> coords;
const std::size_t num_coords{GetImageTypeNumCoordinates(image_type)};
coords.reserve(num_coords);
for (std::size_t i = 0; i < num_coords; ++i) {
coords.push_back(GetRegister(instr.gpr8.Value() + i));
}
return coords;
};
switch (opcode->get().GetId()) {
case OpCode::Id::SULD: {
UNIMPLEMENTED_IF(instr.suldst.mode != Tegra::Shader::SurfaceDataMode::P);
UNIMPLEMENTED_IF(instr.suldst.out_of_bounds_store !=
Tegra::Shader::OutOfBoundsStore::Ignore);
const auto type{instr.suldst.image_type};
auto& image{instr.suldst.is_immediate ? GetImage(instr.image, type)
: GetBindlessImage(instr.gpr39, type)};
image.MarkRead();
u32 indexer = 0;
for (u32 element = 0; element < 4; ++element) {
if (!instr.suldst.IsComponentEnabled(element)) {
continue;
}
MetaImage meta{image, {}, element};
Node value = Operation(OperationCode::ImageLoad, meta, GetCoordinates(type));
SetTemporary(bb, indexer++, std::move(value));
}
for (u32 i = 0; i < indexer; ++i) {
SetRegister(bb, instr.gpr0.Value() + i, GetTemporary(i));
}
break;
}
case OpCode::Id::SUST: {
UNIMPLEMENTED_IF(instr.sust.mode != Tegra::Shader::SurfaceDataMode::P);
UNIMPLEMENTED_IF(instr.sust.out_of_bounds_store != Tegra::Shader::OutOfBoundsStore::Ignore);
UNIMPLEMENTED_IF(instr.sust.component_mask_selector != 0xf); // Ensure we have an RGBA store
UNIMPLEMENTED_IF(instr.suldst.mode != Tegra::Shader::SurfaceDataMode::P);
UNIMPLEMENTED_IF(instr.suldst.out_of_bounds_store !=
Tegra::Shader::OutOfBoundsStore::Ignore);
UNIMPLEMENTED_IF(instr.suldst.component_mask_selector != 0xf); // Ensure we have RGBA
std::vector<Node> values;
constexpr std::size_t hardcoded_size{4};
@@ -53,58 +88,51 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) {
values.push_back(GetRegister(instr.gpr0.Value() + i));
}
std::vector<Node> coords;
const std::size_t num_coords{GetImageTypeNumCoordinates(instr.sust.image_type)};
for (std::size_t i = 0; i < num_coords; ++i) {
coords.push_back(GetRegister(instr.gpr8.Value() + i));
}
const auto type{instr.sust.image_type};
auto& image{instr.sust.is_immediate ? GetImage(instr.image, type)
: GetBindlessImage(instr.gpr39, type)};
const auto type{instr.suldst.image_type};
auto& image{instr.suldst.is_immediate ? GetImage(instr.image, type)
: GetBindlessImage(instr.gpr39, type)};
image.MarkWrite();
MetaImage meta{image, values};
bb.push_back(Operation(OperationCode::ImageStore, meta, std::move(coords)));
MetaImage meta{image, std::move(values)};
bb.push_back(Operation(OperationCode::ImageStore, meta, GetCoordinates(type)));
break;
}
case OpCode::Id::SUATOM: {
UNIMPLEMENTED_IF(instr.suatom_d.is_ba != 0);
Node value = GetRegister(instr.gpr0);
std::vector<Node> coords;
const std::size_t num_coords{GetImageTypeNumCoordinates(instr.sust.image_type)};
for (std::size_t i = 0; i < num_coords; ++i) {
coords.push_back(GetRegister(instr.gpr8.Value() + i));
}
const OperationCode operation_code = [instr] {
switch (instr.suatom_d.operation) {
case Tegra::Shader::ImageAtomicOperation::Add:
return OperationCode::AtomicImageAdd;
case Tegra::Shader::ImageAtomicOperation::Min:
return OperationCode::AtomicImageMin;
case Tegra::Shader::ImageAtomicOperation::Max:
return OperationCode::AtomicImageMax;
case Tegra::Shader::ImageAtomicOperation::And:
return OperationCode::AtomicImageAnd;
case Tegra::Shader::ImageAtomicOperation::Or:
return OperationCode::AtomicImageOr;
case Tegra::Shader::ImageAtomicOperation::Xor:
return OperationCode::AtomicImageXor;
case Tegra::Shader::ImageAtomicOperation::Exch:
return OperationCode::AtomicImageExchange;
switch (instr.suatom_d.operation_type) {
case Tegra::Shader::ImageAtomicOperationType::S32:
case Tegra::Shader::ImageAtomicOperationType::U32:
switch (instr.suatom_d.operation) {
case Tegra::Shader::ImageAtomicOperation::Add:
return OperationCode::AtomicImageAdd;
case Tegra::Shader::ImageAtomicOperation::And:
return OperationCode::AtomicImageAnd;
case Tegra::Shader::ImageAtomicOperation::Or:
return OperationCode::AtomicImageOr;
case Tegra::Shader::ImageAtomicOperation::Xor:
return OperationCode::AtomicImageXor;
case Tegra::Shader::ImageAtomicOperation::Exch:
return OperationCode::AtomicImageExchange;
}
default:
UNIMPLEMENTED_MSG("Unimplemented operation={}",
static_cast<u32>(instr.suatom_d.operation.Value()));
return OperationCode::AtomicImageAdd;
break;
}
UNIMPLEMENTED_MSG("Unimplemented operation={} type={}",
static_cast<u64>(instr.suatom_d.operation.Value()),
static_cast<u64>(instr.suatom_d.operation_type.Value()));
return OperationCode::AtomicImageAdd;
}();
const auto& image{GetImage(instr.image, instr.suatom_d.image_type, instr.suatom_d.size)};
Node value = GetRegister(instr.gpr0);
const auto type = instr.suatom_d.image_type;
auto& image = GetImage(instr.image, type);
image.MarkAtomic();
MetaImage meta{image, {std::move(value)}};
SetRegister(bb, instr.gpr0, Operation(operation_code, meta, std::move(coords)));
SetRegister(bb, instr.gpr0, Operation(operation_code, meta, GetCoordinates(type)));
break;
}
default:
@@ -114,35 +142,32 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) {
return pc;
}
Image& ShaderIR::GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type,
std::optional<Tegra::Shader::ImageAtomicSize> size) {
Image& ShaderIR::GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type) {
const auto offset{static_cast<std::size_t>(image.index.Value())};
if (const auto image = TryUseExistingImage(offset, type, size)) {
if (const auto image = TryUseExistingImage(offset, type)) {
return *image;
}
const std::size_t next_index{used_images.size()};
return used_images.emplace(offset, Image{offset, next_index, type, size}).first->second;
return used_images.emplace(offset, Image{offset, next_index, type}).first->second;
}
Image& ShaderIR::GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type,
std::optional<Tegra::Shader::ImageAtomicSize> size) {
Image& ShaderIR::GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type) {
const Node image_register{GetRegister(reg)};
const auto [base_image, cbuf_index, cbuf_offset]{
TrackCbuf(image_register, global_code, static_cast<s64>(global_code.size()))};
const auto cbuf_key{(static_cast<u64>(cbuf_index) << 32) | static_cast<u64>(cbuf_offset)};
if (const auto image = TryUseExistingImage(cbuf_key, type, size)) {
if (const auto image = TryUseExistingImage(cbuf_key, type)) {
return *image;
}
const std::size_t next_index{used_images.size()};
return used_images.emplace(cbuf_key, Image{cbuf_index, cbuf_offset, next_index, type, size})
return used_images.emplace(cbuf_key, Image{cbuf_index, cbuf_offset, next_index, type})
.first->second;
}
Image* ShaderIR::TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type,
std::optional<Tegra::Shader::ImageAtomicSize> size) {
Image* ShaderIR::TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type) {
auto it = used_images.find(offset);
if (it == used_images.end()) {
return nullptr;
@@ -150,14 +175,6 @@ Image* ShaderIR::TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type,
auto& image = it->second;
ASSERT(image.GetType() == type);
if (size) {
// We know the size, if it's known it has to be the same as before, otherwise we can set it.
if (image.IsSizeKnown()) {
ASSERT(image.GetSize() == size);
} else {
image.SetSize(*size);
}
}
return &image;
}

View File

@@ -149,10 +149,10 @@ enum class OperationCode {
TextureQueryLod, /// (MetaTexture, float[N] coords) -> float4
TexelFetch, /// (MetaTexture, int[N], int) -> float4
ImageStore, /// (MetaImage, int[N] values) -> void
ImageLoad, /// (MetaImage, int[N] coords) -> void
ImageStore, /// (MetaImage, int[N] coords) -> void
AtomicImageAdd, /// (MetaImage, int[N] coords) -> void
AtomicImageMin, /// (MetaImage, int[N] coords) -> void
AtomicImageMax, /// (MetaImage, int[N] coords) -> void
AtomicImageAnd, /// (MetaImage, int[N] coords) -> void
AtomicImageOr, /// (MetaImage, int[N] coords) -> void
AtomicImageXor, /// (MetaImage, int[N] coords) -> void
@@ -294,21 +294,18 @@ private:
class Image final {
public:
constexpr explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type,
std::optional<Tegra::Shader::ImageAtomicSize> size)
: offset{offset}, index{index}, type{type}, is_bindless{false}, size{size} {}
constexpr explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type)
: offset{offset}, index{index}, type{type}, is_bindless{false} {}
constexpr explicit Image(u32 cbuf_index, u32 cbuf_offset, std::size_t index,
Tegra::Shader::ImageType type,
std::optional<Tegra::Shader::ImageAtomicSize> size)
Tegra::Shader::ImageType type)
: offset{(static_cast<u64>(cbuf_index) << 32) | cbuf_offset}, index{index}, type{type},
is_bindless{true}, size{size} {}
is_bindless{true} {}
constexpr explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type,
bool is_bindless, bool is_written, bool is_read,
std::optional<Tegra::Shader::ImageAtomicSize> size)
bool is_bindless, bool is_written, bool is_read, bool is_atomic)
: offset{offset}, index{index}, type{type}, is_bindless{is_bindless},
is_written{is_written}, is_read{is_read}, size{size} {}
is_written{is_written}, is_read{is_read}, is_atomic{is_atomic} {}
void MarkWrite() {
is_written = true;
@@ -318,8 +315,10 @@ public:
is_read = true;
}
void SetSize(Tegra::Shader::ImageAtomicSize size_) {
size = size_;
void MarkAtomic() {
MarkWrite();
MarkRead();
is_atomic = true;
}
constexpr std::size_t GetOffset() const {
@@ -346,21 +345,17 @@ public:
return is_read;
}
constexpr bool IsAtomic() const {
return is_atomic;
}
constexpr std::pair<u32, u32> GetBindlessCBuf() const {
return {static_cast<u32>(offset >> 32), static_cast<u32>(offset)};
}
constexpr bool IsSizeKnown() const {
return size.has_value();
}
constexpr Tegra::Shader::ImageAtomicSize GetSize() const {
return size.value();
}
constexpr bool operator<(const Image& rhs) const {
return std::tie(offset, index, type, size, is_bindless) <
std::tie(rhs.offset, rhs.index, rhs.type, rhs.size, rhs.is_bindless);
return std::tie(offset, index, type, is_bindless) <
std::tie(rhs.offset, rhs.index, rhs.type, rhs.is_bindless);
}
private:
@@ -370,7 +365,7 @@ private:
bool is_bindless{};
bool is_written{};
bool is_read{};
std::optional<Tegra::Shader::ImageAtomicSize> size{};
bool is_atomic{};
};
struct GlobalMemoryBase {
@@ -402,6 +397,7 @@ struct MetaTexture {
struct MetaImage {
const Image& image;
std::vector<Node> values;
u32 element{};
};
/// Parameters that modify an operation but are not part of any particular operand

View File

@@ -284,16 +284,13 @@ private:
bool is_shadow);
/// Accesses an image.
Image& GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type,
std::optional<Tegra::Shader::ImageAtomicSize> size = {});
Image& GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type);
/// Access a bindless image sampler.
Image& GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type,
std::optional<Tegra::Shader::ImageAtomicSize> size = {});
Image& GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type);
/// Tries to access an existing image, updating it's state as needed
Image* TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type,
std::optional<Tegra::Shader::ImageAtomicSize> size);
Image* TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type);
/// Extracts a sequence of bits from a node
Node BitfieldExtract(Node value, u32 offset, u32 bits);