neurotic optimizations (overhauled depth pyramid and bloom to ffx-sdp), more fixes

This commit is contained in:
ecker 2026-04-21 20:49:47 -05:00
parent fd9036ee39
commit 6d05ab865a
27 changed files with 3986 additions and 434 deletions

View File

@ -110,7 +110,7 @@
"default stage buffers": true,
"default defer buffer destroy": true,
"default command buffer immediate": true,
"multithreaded recording": true
"multithreaded recording": false
},
"pipelines": {
"deferred": true,
@ -147,7 +147,8 @@
"deviceCoherentMemory",
"robustBufferAccess",
"samplerAnisotropy",
"sampleRateShading"
"sampleRateShading",
"samplerFilterMinmax"
],
"featureChain": []
},

View File

@ -1,9 +1,9 @@
{
// "import": "./rp_downtown_v2.json"
"import": "./ss2_medsci1.json"
// "import": "./ss2_medsci1.json"
// "import": "./test_grid.json"
// "import": "./sh2_mcdonalds.json"
// "import": "./animal_crossing.json"
// "import": "./mds_mcdonalds.json"
"import": "./mds_mcdonalds.json"
// "import": "./gm_construct.json"
}

View File

@ -44,6 +44,7 @@ void gammaCorrect( inout vec3 color, float gamma ) {
}
void toneMap( inout vec4 color, float exposure ) { toneMap(color.rgb, exposure); }
void gammaCorrect( inout vec4 color, float gamma ) { gammaCorrect(color.rgb, gamma); }
float luma( vec3 color ) { return dot(color, vec3(0.2126, 0.7152, 0.0722)); }
//
uint tea(uint val0, uint val1) {
uint v0 = val0;
@ -152,6 +153,7 @@ vec3 decodeSrgb(vec3 rgb) {
const vec3 c = step(vec3(0.04045), rgb);
return mix(a, b, c);
}
#if !SPD && (DEFERRED || FRAGMENT || COMPUTE || RT)
bool validTextureIndex( int textureIndex ) {
return 0 <= textureIndex && textureIndex < MAX_TEXTURES;
}
@ -160,7 +162,6 @@ bool validCubemapIndex( int textureIndex ) {
return 0 <= textureIndex && textureIndex < MAX_CUBEMAPS;
}
#endif
#if !BLOOM && (DEFERRED || FRAGMENT || COMPUTE || RT)
bool validTextureIndex( uint id ) {
return 0 <= id && id < MAX_TEXTURES;
}

View File

@ -1,65 +0,0 @@
#version 450
#pragma shader_stage(compute)
#define COMPUTE 1
#define TEXTURES 0
#define CUBEMAPS 0
#define BLOOM 1
layout (local_size_x = 16, local_size_y = 16, local_size_z = 1) in;
layout( push_constant ) uniform PushBlock {
uint eye;
uint mode;
} PushConstant;
layout (binding = 0) uniform UBO {
float threshold;
float smoothness;
uint size;
float padding1;
float weights[32];
} ubo;
layout (binding = 1, rgba16f) uniform image2D imageColor;
layout (binding = 2, rgba16f) uniform image2D imageBloom;
layout (binding = 3, rgba16f) uniform image2D imagePingPong;
#include "../../common/macros.h"
#include "../../common/structs.h"
#include "../../common/functions.h"
void main() {
const uint mode = PushConstant.mode;
const ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
const ivec2 size = imageSize( imageColor );
if ( texel.x >= size.x || texel.y >= size.y ) return;
if ( mode == 0 ) { // fill bloom
vec3 result = imageLoad( imageColor, texel ).rgb;
float brightness = dot(result, vec3(0.2126, 0.7152, 0.0722));
if( brightness < ubo.threshold ) result = vec3(0.0);
imageStore(imageBloom, texel, vec4(result, 1.0));
} else if ( mode == 1 ) { // bloom horizontal
vec3 result = imageLoad( imageBloom, texel ).rgb * ubo.weights[0];
for ( int i = 1; i < int(ubo.size); ++i ) {
vec3 c1 = imageLoad( imageBloom, texel + ivec2(i, 0) ).rgb;
vec3 c2 = imageLoad( imageBloom, texel - ivec2(i, 0) ).rgb;
result += (c1 + c2) * ubo.weights[i];
}
imageStore( imagePingPong, texel, vec4(result, 1.0) );
} else if ( mode == 2 ) { // bloom vertical
vec3 result = imageLoad( imagePingPong, texel ).rgb * ubo.weights[0];
for( int i = 1; i < int(ubo.size); ++i ) {
vec3 c1 = imageLoad( imagePingPong, texel + ivec2(0, i) ).rgb;
vec3 c2 = imageLoad( imagePingPong, texel - ivec2(0, i) ).rgb;
result += (c1 + c2) * ubo.weights[i];
}
imageStore(imageBloom, texel, vec4(result, 1.0));
} else if ( mode == 3 ) { // combine
vec3 base = imageLoad( imageColor, texel ).rgb;
vec3 bloom = imageLoad( imageBloom, texel ).rgb;
imageStore( imageColor, texel, vec4(base + bloom, 1.0) );
}
}

View File

@ -0,0 +1,131 @@
#version 450
#pragma shader_stage(compute)
#extension GL_KHR_shader_subgroup_quad : require
#extension GL_KHR_shader_subgroup_arithmetic : require
#extension GL_EXT_samplerless_texture_functions : enable
#define COMPUTE 1
#define SPD 1
#include "../../common/macros.h"
#include "../../common/structs.h"
#include "../../common/functions.h"
layout (local_size_x = 256, local_size_y = 1, local_size_z = 1) in;
layout (constant_id = 0) const uint MIPS = 6;
layout(push_constant) uniform PushBlock {
uint mips;
uint numWorkGroups;
uint workGroupOffset;
} PushConstant_;
layout (binding = 0, rgba16f) uniform image2D imageColor;
layout (binding = 1, rgba16f) uniform image2D imageBright; // yucky
layout (binding = 2, rgba16f) coherent uniform image2D outImage[MIPS];
layout (binding = 3, std430) buffer AtomicCounter {
uint counter;
} spdCounter;
layout (binding = 4) uniform UBO {
float threshold;
float smoothness;
uint size;
float padding1;
float weights[32];
} ubo;
#define A_GLSL 1
#define A_GPU 1
#define SPD_NO_WAVE_OPERATIONS 0
#include "../../ext/ffx_a.h"
shared AU1 spd_counter;
shared AF4 spd_intermediate[16][16];
vec3 applySoftKnee(vec3 color, float luminance) {
float rq = clamp(luminance - ubo.threshold + ubo.smoothness, 0.0, 2.0 * ubo.smoothness);
rq = (rq * rq) / (4.0 * ubo.smoothness + 0.0001);
float value = max(rq, luminance - ubo.threshold);
return color * (value / (max(luminance, 0.0001)));
}
AF4 SpdLoadSourceImage(ASU2 p, AU1 slice) {
ivec2 size = imageSize(imageColor);
// sample color if in bound, else black
vec3 c0 = p.x < size.x && p.y < size.y ? imageLoad(imageColor, p + ivec2(0, 0)).rgb : vec3(0.0);
vec3 c1 = p.x + 1 < size.x && p.y < size.y ? imageLoad(imageColor, p + ivec2(1, 0)).rgb : vec3(0.0);
vec3 c2 = p.x < size.x && p.y + 1 < size.y ? imageLoad(imageColor, p + ivec2(0, 1)).rgb : vec3(0.0);
vec3 c3 = p.x + 1 < size.x && p.y + 1 < size.y ? imageLoad(imageColor, p + ivec2(1, 1)).rgb : vec3(0.0);
// get luma
float b0 = luma(c0);
float b1 = luma(c1);
float b2 = luma(c2);
float b3 = luma(c3);
// soften
c0 = applySoftKnee(c0, b0);
c1 = applySoftKnee(c1, b1);
c2 = applySoftKnee(c2, b2);
c3 = applySoftKnee(c3, b3);
// karis luma weighted average
float w0 = 1.0 / (b0 + 1.0);
float w1 = 1.0 / (b1 + 1.0);
float w2 = 1.0 / (b2 + 1.0);
float w3 = 1.0 / (b3 + 1.0);
float inv_wsum = 1.0 / (w0 + w1 + w2 + w3);
// store to mip 0
if (p.x < size.x && p.y < size.y) imageStore(outImage[0], p + ivec2(0, 0), vec4(c0, 1.0));
if (p.x + 1 < size.x && p.y < size.y) imageStore(outImage[0], p + ivec2(1, 0), vec4(c1, 1.0));
if (p.x < size.x && p.y + 1 < size.y) imageStore(outImage[0], p + ivec2(0, 1), vec4(c2, 1.0));
if (p.x + 1 < size.x && p.y + 1 < size.y) imageStore(outImage[0], p + ivec2(1, 1), vec4(c3, 1.0));
// average
return AF4((c0 * w0 + c1 * w1 + c2 * w2 + c3 * w3) * inv_wsum, 1.0);
}
AF4 SpdLoad(ASU2 p, AU1 slice) {
uint loadMip = min(6u - 1, MIPS - 1);
return imageLoad(outImage[loadMip + 1], p);
}
void SpdStore(ASU2 p, AF4 value, AU1 mip, AU1 slice) {
if ( mip + 1 < MIPS ) {
imageStore(outImage[mip + 1], p, value);
}
}
// average filter
AF4 SpdReduce4(AF4 v0, AF4 v1, AF4 v2, AF4 v3) {
return (v0 + v1 + v2 + v3) * 0.25;
}
AF4 SpdLoadIntermediate(AU1 x, AU1 y) { return spd_intermediate[x][y]; }
void SpdStoreIntermediate(AU1 x, AU1 y, AF4 value) { spd_intermediate[x][y] = value; }
void SpdIncreaseAtomicCounter(AU1 slice) { spd_counter = atomicAdd(spdCounter.counter, 1); }
AU1 SpdGetAtomicCounter() { return spd_counter; }
void SpdResetAtomicCounter(AU1 slice) { spdCounter.counter = 0; }
#include "../../ext/ffx_spd.h"
void main() {
SpdDownsample(
AU2(gl_WorkGroupID.xy),
AU1(gl_LocalInvocationIndex),
AU1(PushConstant_.mips - 1),
AU1(PushConstant_.numWorkGroups),
AU1(PushConstant_.workGroupOffset)
);
}

View File

@ -0,0 +1,60 @@
#version 450
#pragma shader_stage(compute)
#define COMPUTE 1
layout (local_size_x = 16, local_size_y = 16, local_size_z = 1) in;
layout (constant_id = 0) const uint MIPS = 6;
layout (binding = 0, rgba16f) uniform image2D imageColor;
layout (binding = 1) uniform sampler2D samplerBloom;
layout (binding = 2) uniform UBO {
float threshold;
float smoothness;
uint size;
float padding1;
float weights[32];
} ubo;
// 9-tap bilinear tent filter
vec3 tentFilter(sampler2D tex, vec2 uv, float lod) {
vec2 texSize = vec2(textureSize(tex, int(lod)));
vec4 d = (1.0 / texSize.xyxy) * vec4(1.0, 1.0, -1.0, 0.0);
vec3 s = textureLod(tex, uv - d.xy, lod).rgb;
s += textureLod(tex, uv - d.wy, lod).rgb * 2.0;
s += textureLod(tex, uv - d.zy, lod).rgb;
s += textureLod(tex, uv + d.zw, lod).rgb * 2.0;
s += textureLod(tex, uv, lod).rgb * 4.0;
s += textureLod(tex, uv + d.xw, lod).rgb * 2.0;
s += textureLod(tex, uv + d.zy, lod).rgb;
s += textureLod(tex, uv + d.wy, lod).rgb * 2.0;
s += textureLod(tex, uv + d.xy, lod).rgb;
return s * (1.0 / 16.0);
}
void main() {
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
ivec2 size = imageSize(imageColor);
if ( texel.x >= size.x || texel.y >= size.y ) return;
vec2 uv = (vec2(texel) + 0.5) / vec2(size);
vec3 bloomAcc = vec3(0.0);
float weightSum = 0.0;
for ( uint i = 0; i < min(MIPS, ubo.size); ++i ) {
float w = ubo.weights[i];
bloomAcc += textureLod(samplerBloom, uv, float(i)).rgb * w;
//bloomAcc += tentFilter(samplerBloom, uv, float(i)) * w;
weightSum += w;
}
if ( weightSum > 0.0 ) bloomAcc /= weightSum;
vec3 base = imageLoad( imageColor, texel ).rgb;
imageStore( imageColor, texel, vec4(base + bloomAcc, 1.0) );
}

View File

@ -159,7 +159,7 @@ void postProcess() {
#if FOG
fog( surface.ray, surface.fragment.rgb, surface.fragment.a );
#endif
float brightness = dot(surface.fragment.rgb, vec3(0.2126, 0.7152, 0.0722));
float brightness = luma(surface.fragment.rgb);
bool bloom = brightness > ubo.settings.bloom.threshold;
//if ( bloom ) toneMap( surface.fragment.rgb, brightness );
vec4 outFragColor = vec4(surface.fragment.rgb, 1.0);
@ -184,7 +184,7 @@ void postProcess() {
}
IMAGE_STORE( imageColor, outFragColor );
IMAGE_STORE( imageBright, outFragBright );
//IMAGE_STORE( imageBright, outFragBright );
IMAGE_STORE( imageMotion, vec4(outFragMotion, 0, 0) );
}

View File

@ -1,35 +1,96 @@
#version 450
#pragma shader_stage(compute)
//#extension GL_EXT_nonuniform_qualifier : enable
layout (constant_id = 0) const uint MIPS = 6;
layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
#extension GL_KHR_shader_subgroup_quad : require
#extension GL_KHR_shader_subgroup_arithmetic : require
#extension GL_EXT_samplerless_texture_functions : enable
#define COMPUTE 1
#define SPD 1
#include "../../common/macros.h"
#include "../../common/structs.h"
#include "../../common/functions.h"
layout( push_constant ) uniform PushBlock {
uint _;
uint pass;
} PushConstant;
layout (local_size_x = 256, local_size_y = 1, local_size_z = 1) in;
layout (constant_id = 0) const uint MIPS = 6;
layout(push_constant) uniform PushBlock {
uint mips;
uint numWorkGroups;
uint workGroupOffset;
} PushConstant_;
layout (binding = 0) uniform sampler2D samplerDepth;
layout (binding = 1) uniform sampler2D inImage[MIPS];
layout (binding = 2, r32f) uniform writeonly image2D outImage[MIPS];
layout (binding = 1, r32f) coherent uniform image2D outImage[MIPS];
layout (binding = 2, std430) buffer AtomicCounter {
uint counter;
} spdCounter;
#define A_GLSL 1
#define A_GPU 1
#define SPD_NO_WAVE_OPERATIONS 0
#include "../../ext/ffx_a.h"
shared AU1 spd_counter;
shared AF1 spd_intermediate[16][16];
AF4 SpdLoadSourceImage(ASU2 p, AU1 slice) {
ivec2 size = imageSize(outImage[0]);
// sample depth if in bound, else 0 (0 for reverse-z projection, use 1 if normal projection)
float d0 = p.x < size.x && p.y < size.y ? texelFetch(samplerDepth, p + ivec2(0, 0), 0).x : 0.0;
float d1 = p.x + 1 < size.x && p.y < size.y ? texelFetch(samplerDepth, p + ivec2(1, 0), 0).x : 0.0;
float d2 = p.x < size.x && p.y + 1 < size.y ? texelFetch(samplerDepth, p + ivec2(0, 1), 0).x : 0.0;
float d3 = p.x + 1 < size.x && p.y + 1 < size.y ? texelFetch(samplerDepth, p + ivec2(1, 1), 0).x : 0.0;
// store to mip 0
if (p.x < size.x && p.y < size.y) imageStore(outImage[0], p + ivec2(0, 0), vec4(d0));
if (p.x + 1 < size.x && p.y < size.y) imageStore(outImage[0], p + ivec2(1, 0), vec4(d1));
if (p.x < size.x && p.y + 1 < size.y) imageStore(outImage[0], p + ivec2(0, 1), vec4(d2));
if (p.x + 1 < size.x && p.y + 1 < size.y) imageStore(outImage[0], p + ivec2(1, 1), vec4(d3));
return AF4(d0, d1, d2, d3);
}
AF4 SpdLoad(ASU2 p, AU1 slice) {
uint loadMip = min(6u, MIPS - 1);
float d = imageLoad(outImage[loadMip], p).r;
return AF4(d, d, d, d);
}
void SpdStore(ASU2 p, AF4 value, AU1 mip, AU1 slice) {
if ( mip + 1 < MIPS ) {
imageStore(outImage[mip + 1], p, vec4(value.x));
}
}
AF4 SpdLoadIntermediate(AU1 x, AU1 y) {
float d = spd_intermediate[x][y];
return AF4(d, d, d, d);
}
void SpdStoreIntermediate(AU1 x, AU1 y, AF4 value) { spd_intermediate[x][y] = value.x; }
void SpdIncreaseAtomicCounter(AU1 slice) { spd_counter = atomicAdd(spdCounter.counter, 1); }
AU1 SpdGetAtomicCounter() { return spd_counter; }
void SpdResetAtomicCounter(AU1 slice) { spdCounter.counter = 0; }
// min filter
AF4 SpdReduce4(AF4 v0, AF4 v1, AF4 v2, AF4 v3) {
float minVal = min(min(v0.x, v1.x), min(v2.x, v3.x));
return AF4(minVal, minVal, minVal, minVal);
}
#include "../../ext/ffx_spd.h"
void main() {
int mip = int(PushConstant.pass);
float depth;
ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
if ( mip == 0 ) {
depth = texelFetch(samplerDepth, pos, 0).r;
} else {
depth = texture(inImage[mip - 1], (vec2(gl_GlobalInvocationID.xy) + vec2(0.5)) / imageSize( outImage[mip] )).x;
}
imageStore(outImage[mip], pos, vec4(depth));
SpdDownsample(
AU2(gl_WorkGroupID.xy),
AU1(gl_LocalInvocationIndex),
AU1(PushConstant_.mips - 1),
AU1(PushConstant_.numWorkGroups),
AU1(PushConstant_.workGroupOffset)
);
}

1907
bin/data/shaders/ext/ffx_a.h Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@
#extension GL_EXT_samplerless_texture_functions : enable
layout (constant_id = 0) const uint PASSES = 6;
layout (local_size_x = 32, local_size_y = 1, local_size_z = 1) in;
layout (local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
#define COMPUTE 1
#define QUERY_MIPMAPS 1
@ -73,106 +73,100 @@ layout (std140, binding = 3) buffer Objects {
layout (binding = 4) uniform sampler2D samplerDepth;
shared vec4 sharedPlanes[PASSES][6];
vec4 normalizePlane( vec4 p ) {
return p / length(p.xyz);
}
bool frustumCull( uint id ) {
if ( PushConstant.passes == 0 ) return true;
const DrawCommand drawCommand = drawCommands[id];
const Instance instance = instances[drawCommand.instanceID];
const Object object = objects[instance.objectID];
if ( drawCommand.indices == 0 || drawCommand.vertices == 0 ) return false;
bool visible = false;
for ( uint pass = 0; pass < PushConstant.passes; ++pass ) {
mat4 mat = camera.viewport[pass].projection * camera.viewport[pass].view * object.model;
vec4 planes[6]; {
for (int i = 0; i < 3; ++i)
for (int j = 0; j < 2; ++j) {
planes[i*2+j].x = mat[0][3] + (j == 0 ? mat[0][i] : -mat[0][i]);
planes[i*2+j].y = mat[1][3] + (j == 0 ? mat[1][i] : -mat[1][i]);
planes[i*2+j].z = mat[2][3] + (j == 0 ? mat[2][i] : -mat[2][i]);
planes[i*2+j].w = mat[3][3] + (j == 0 ? mat[3][i] : -mat[3][i]);
planes[i*2+j] = normalizePlane( planes[i*2+j] );
}
}
bool insideFrustum = true;
for ( uint p = 0; p < 6; ++p ) {
float d = max(instance.bounds.min.x * planes[p].x, instance.bounds.max.x * planes[p].x)
+ max(instance.bounds.min.y * planes[p].y, instance.bounds.max.y * planes[p].y)
+ max(instance.bounds.min.z * planes[p].z, instance.bounds.max.z * planes[p].z);
if (d < -planes[p].w) {
insideFrustum = false;
break;
}
}
if ( insideFrustum ) {
visible = true;
break;
}
}
return visible;
}
bool occlusionCull( uint id ) {
if ( PushConstant.passes == 0 ) return true;
const DrawCommand drawCommand = drawCommands[id];
const Instance instance = instances[drawCommand.instanceID];
const Object object = objects[instance.objectID];
bool visible = false;
for ( uint pass = 0; pass < PushConstant.passes; ++pass ) {
vec4 aabb;
vec4 sphere = aabbToSphere( instance.bounds );
float scale = length(object.model[0].xyz);
vec3 center = (camera.viewport[pass].view * object.model * vec4(sphere.xyz, 1)).xyz;
float radius = scale * sphere.w;
mat4 proj = camera.viewport[pass].projection;
float znear = proj[3][2];
float P00 = proj[0][0];
float P11 = proj[1][1];
if ( projectSphere( center, radius, znear, P00, P11, aabb ) ) {
vec2 pyramidSize = vec2(textureSize( samplerDepth, 0 ));
float width = (aabb.z - aabb.x) * pyramidSize.x;
float height = (aabb.w - aabb.y) * pyramidSize.y;
float level = max(0.0, floor(log2(max(width, height))));
float d1 = textureLod(samplerDepth, vec2(aabb.x, aabb.y), level).x;
float d2 = textureLod(samplerDepth, vec2(aabb.z, aabb.y), level).x;
float d3 = textureLod(samplerDepth, vec2(aabb.x, aabb.w), level).x;
float d4 = textureLod(samplerDepth, vec2(aabb.z, aabb.w), level).x;
float depth = min(min(d1, d2), min(d3, d4)); // min for reverse-z projection, max for standard
float depthSphere = znear / (center.z - radius);
if ( depthSphere >= depth - DEPTH_BIAS ) {
visible = true;
break;
}
} else {
visible = true;
break;
}
}
return visible;
return p / length(p.xyz);
}
void main() {
const uint gID = gl_GlobalInvocationID.x;
if ( !(0 <= gID && gID < drawCommands.length()) ) return;
const uint gID = gl_GlobalInvocationID.x;
const uint lID = gl_LocalInvocationIndex;
bool visible = frustumCull( gID );
if ( visible ) visible = occlusionCull( gID );
drawCommands[gID].instances = visible ? 1 : 0;
if ( lID == 0 ) {
for (uint pass = 0; pass < PushConstant.passes; ++pass) {
mat4 mat = camera.viewport[pass].projection * camera.viewport[pass].view;
for (int i = 0; i < 3; ++i)
for (int j = 0; j < 2; ++j) {
sharedPlanes[pass][i*2+j].x = mat[0][3] + (j == 0 ? mat[0][i] : -mat[0][i]);
sharedPlanes[pass][i*2+j].y = mat[1][3] + (j == 0 ? mat[1][i] : -mat[1][i]);
sharedPlanes[pass][i*2+j].z = mat[2][3] + (j == 0 ? mat[2][i] : -mat[2][i]);
sharedPlanes[pass][i*2+j].w = mat[3][3] + (j == 0 ? mat[3][i] : -mat[3][i]);
sharedPlanes[pass][i*2+j] = normalizePlane( sharedPlanes[pass][i*2+j] );
}
}
}
barrier();
if ( gID >= drawCommands.length() ) return;
const DrawCommand drawCommand = drawCommands[gID];
if ( drawCommand.indices == 0 || drawCommand.vertices == 0 ) return;
const Instance instance = instances[drawCommand.instanceID];
const Object object = objects[instance.objectID];
vec4 sphere = aabbToSphere( instance.bounds );
vec3 worldCenter = (object.model * vec4(sphere.xyz, 1.0)).xyz;
float scaleX = length(object.model[0].xyz);
float scaleY = length(object.model[1].xyz);
float scaleZ = length(object.model[2].xyz);
float maxScale = max(max(scaleX, scaleY), scaleZ);
float worldRadius = sphere.w * maxScale;
bool isVisible = false;
for ( uint pass = 0; pass < PushConstant.passes; ++pass ) {
bool insideFrustum = true;
for ( int p = 0; p < 6; ++p ) {
if ( dot(sharedPlanes[pass][p].xyz, worldCenter) + sharedPlanes[pass][p].w < -worldRadius ) {
insideFrustum = false;
break;
}
}
if ( insideFrustum ) {
isVisible = true;
break;
}
}
if ( isVisible ) {
isVisible = false;
for ( uint pass = 0; pass < PushConstant.passes; ++pass ) {
vec4 aabb;
vec3 viewCenter = ( camera.viewport[pass].view * vec4(worldCenter, 1.0) ).xyz;
mat4 proj = camera.viewport[pass].projection;
float znear = proj[3][2];
float P00 = proj[0][0];
float P11 = proj[1][1];
if ( projectSphere(viewCenter, worldRadius, znear, P00, P11, aabb) ) {
vec2 pyramidSize = vec2(textureSize( samplerDepth, 0 ));
float width = (aabb.z - aabb.x) * pyramidSize.x;
float height = (aabb.w - aabb.y) * pyramidSize.y;
float level = floor(log2(max(width, height)));
level = max(0.0, level);
float d1 = textureLod(samplerDepth, vec2(aabb.x, aabb.y), level).x;
float d2 = textureLod(samplerDepth, vec2(aabb.z, aabb.y), level).x;
float d3 = textureLod(samplerDepth, vec2(aabb.x, aabb.w), level).x;
float d4 = textureLod(samplerDepth, vec2(aabb.z, aabb.w), level).x;
float depth = min(min(d1, d2), min(d3, d4));
float depthSphere = znear / (viewCenter.z - worldRadius);
if ( depthSphere >= depth - DEPTH_BIAS ) {
isVisible = true;
break;
}
} else {
isVisible = true;
break;
}
}
}
drawCommands[gID].instances = isVisible ? 1 : 0;
}

View File

@ -390,7 +390,7 @@ void main() {
#endif
{
#if BLOOM
float brightness = dot(surface.fragment.rgb, vec3(0.2126, 0.7152, 0.0722));
float brightness = luma(surface.fragment.rgb);
vec4 outFragBright = brightness > ubo.threshold ? vec4(surface.fragment.rgb, 1.0) : vec4(0, 0, 0, 1);
// imageStore(outImage, ivec2(gl_LaunchIDEXT.xy), outFragBright);
#endif

View File

@ -111,6 +111,8 @@ namespace pod {
uf::renderer::Buffer material;
uf::renderer::Buffer texture;
uf::renderer::Buffer light;
uf::renderer::Texture2D depthPyramid;
} buffers;
}/* storage*/;
};

View File

@ -28,6 +28,8 @@ namespace ext {
operator VkCommandBuffer() { return handle; }
};
struct Texture;
struct UF_API Device {
VkInstance instance;
VkDebugUtilsMessengerEXT debugMessenger;
@ -86,6 +88,7 @@ namespace ext {
uf::stl::vector<Buffer> buffers;
uf::stl::vector<AccelerationStructure> ass;
uf::stl::vector<Texture> textures;
} transient;
struct {

View File

@ -42,8 +42,8 @@ namespace ext {
void record( const Graphic& graphic, const GraphicDescriptor& descriptor, VkCommandBuffer, size_t = 0, size_t = 0, size_t = 0 ) const;
void destroy();
uf::stl::vector<Shader*> getShaders( uf::stl::vector<Shader>& );
uf::stl::vector<const Shader*> getShaders( const uf::stl::vector<Shader>& ) const;
uf::stl::vector<Shader*> getShaders( uf::stl::vector<Shader>&, const uf::stl::string& = "" );
uf::stl::vector<const Shader*> getShaders( const uf::stl::vector<Shader>&, const uf::stl::string& = "" ) const;
void collectBuffers( const Shader& shader, const RenderMode& renderMode, const Graphic& graphic, const std::function<void(const Buffer&)>& lambda ) const;
};

View File

@ -138,7 +138,7 @@ namespace ext {
inline void update( uf::Image& image, uint32_t layer = 1 ) { return this->update(image, this->imageLayout, layer); }
inline void update( void* data, VkDeviceSize size, uint32_t layer = 1 ) { return this->update(data, size, this->imageLayout, layer); }
void generateMipmaps(VkCommandBuffer commandBuffer, uint32_t layer = 1);
void generateMipmaps(VkCommandBuffer commandBuffer, uint32_t layer = 0);
void fromBuffers( void* buffer, VkDeviceSize bufferSize, VkFormat format, uint32_t texWidth, uint32_t texHeight, uint32_t texDepth, uint32_t layers, VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT, VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL );
inline void fromBuffers( void* buffer, VkDeviceSize bufferSize, VkFormat format, uint32_t texWidth, uint32_t texHeight, VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT, VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ) { return this->fromBuffers(buffer, bufferSize, format, texWidth, texHeight, 1, 1, imageUsageFlags, imageLayout); }

View File

@ -22,7 +22,7 @@
#define VK_DEFAULT_STAGE_BUFFERS ext::vulkan::settings::defaultStageBuffers
#define VK_DEFAULT_DEFER_BUFFER_DESTROY ext::vulkan::settings::defaultDeferBufferDestroy
#define VK_DEFAULT_COMMAND_BUFFER_IMMEDIATE ext::vulkan::settings::defaultCommandBufferImmediate
#define VK_UBO_USE_N_BUFFERS 1
#define VK_UBO_USE_N_BUFFERS 0
namespace ext {
namespace vulkan {

View File

@ -182,10 +182,6 @@ namespace ext {
extern UF_API uint32_t frameSkip;
}
namespace gc {
extern UF_API uf::stl::vector<ext::vulkan::Texture> textures;
}
extern UF_API Device device;
extern UF_API Allocator allocator;

View File

@ -938,8 +938,9 @@ void ext::ExtSceneBehavior::bindBuffers( uf::Object& self, uf::renderer::Graphic
#if UF_USE_VULKAN
// only update this when requested
// done outside of deserialize because the rendermode might not be initialized in time
if ( uf::renderer::settings::pipelines::bloom && metadata.bloom.outOfDate && graphic.material.hasShader("compute", "bloom") ) {
auto& shader = graphic.material.getShader("compute", "bloom");
if ( uf::renderer::settings::pipelines::bloom && metadata.bloom.outOfDate && graphic.material.hasShader("compute", "bloom-down") ) {
auto& shaderDown = graphic.material.getShader("compute", "bloom-down");
auto& shaderUp = graphic.material.getShader("compute", "bloom-up");
struct UniformDescriptor {
float threshold;
@ -974,7 +975,12 @@ void ext::ExtSceneBehavior::bindBuffers( uf::Object& self, uf::renderer::Graphic
for ( auto i = 0; i < uniforms.size; ++i ) uniforms.weights[i] = tempWeights[i] / sum;
metadata.bloom.outOfDate = false;
if ( shader.hasUniform("UBO") ) shader.updateBuffer( (const void*) &uniforms, sizeof(uniforms), shader.getUniformBuffer("UBO") );
if ( shaderDown.hasUniform("UBO") ) {
shaderDown.updateBuffer( (const void*) &uniforms, sizeof(uniforms), shaderDown.getUniformBuffer("UBO") );
}
if ( shaderUp.hasUniform("UBO") ) {
shaderUp.updateBuffer( (const void*) &uniforms, sizeof(uniforms), shaderUp.getUniformBuffer("UBO") );
}
}
struct UniformDescriptor {

View File

@ -196,7 +196,6 @@ namespace {
// compute shader
auto& shader = graphic.material.getShader("compute", uf::renderer::settings::pipelines::names::culling);
shader.aliasAttachment("depthPyramid");
}
// vxgi pipeline
if ( uf::renderer::settings::pipelines::vxgi ) {
@ -479,6 +478,9 @@ namespace {
shader.aliasBuffer( "indirect", *indirect );
shader.aliasBuffer( "instance", storage.buffers.instance );
shader.aliasBuffer( "object", storage.buffers.object );
shader.textures.clear();
shader.textures.emplace_back().aliasTexture( storage.buffers.depthPyramid );
}
// vxgi pipeline
@ -1532,10 +1534,12 @@ void uf::graph::destroy( uf::Object& object, bool soft ) {
void uf::graph::destroy( pod::Graph::Storage& storage, bool soft ) {
soft = false;
#if UF_USE_VULKAN
/*
for ( auto& texture : uf::renderer::gc::textures ) {
texture.destroy( false );
}
uf::renderer::gc::textures.clear();
*/
#endif
// cleanup graphic handles

View File

@ -41,7 +41,7 @@ void ext::vulkan::Pipeline::initialize( const Graphic& graphic, const GraphicDes
this->metadata.type = descriptor.pipeline;
Device& device = *graphic.device;
auto shaders = getShaders( graphic.material.shaders );
auto shaders = getShaders( graphic.material.shaders, descriptor.pipeline );
assert( shaders.size() > 0 );
uint32_t subpass = descriptor.subpass;
@ -397,7 +397,10 @@ void ext::vulkan::Pipeline::record( const Graphic& graphic, VkCommandBuffer comm
return record( graphic, descriptor, commandBuffer, pass, draw, offset );
}
void ext::vulkan::Pipeline::record( const Graphic& graphic, const GraphicDescriptor& descriptor, VkCommandBuffer commandBuffer, size_t pass, size_t draw, size_t offset ) const {
auto shaders = getShaders( graphic.material.shaders );
auto shaders = getShaders( graphic.material.shaders, descriptor.pipeline );
for ( auto i = 0; i < shaders.size(); ++i ) {
// UF_MSG_DEBUG("{} | {}: {}", descriptor.pipeline, i, shaders[i]->filename);
}
// create dynamic offset ranges
static thread_local uf::stl::vector<uint32_t> dynamicOffsets;
@ -427,6 +430,7 @@ void ext::vulkan::Pipeline::record( const Graphic& graphic, const GraphicDescrip
else continue;
}
// automatically bind to our default push constants
if ( shader->metadata.definitions.pushConstants.count("PushConstant") > 0 ) {
struct PushConstant {
uint32_t pass;
@ -450,7 +454,10 @@ void ext::vulkan::Pipeline::record( const Graphic& graphic, const GraphicDescrip
}
// no matching bind point for shaders, skip
if ( !bound ) return;
if ( !bound ) {
UF_MSG_DEBUG("No shaders found to bind...");
return;
}
// Bind descriptor sets describing shader binding points
#if VK_UBO_USE_N_BUFFERS
@ -506,7 +513,7 @@ void ext::vulkan::Pipeline::update( const Graphic& graphic, const GraphicDescrip
RenderMode& renderMode = ext::vulkan::getRenderMode(descriptor.renderMode, true);
auto& renderTarget = renderMode.getRenderTarget(/*descriptor.renderTarget*/);
auto shaders = getShaders( graphic.material.shaders );
auto shaders = getShaders( graphic.material.shaders, descriptor.pipeline );
uf::stl::vector<VkWriteDescriptorSet> writeDescriptorSets;
uf::stl::vector<uf::renderer::AccelerationStructure> tlases;
@ -947,32 +954,32 @@ void ext::vulkan::Pipeline::destroy() {
}
*/
}
uf::stl::vector<ext::vulkan::Shader*> ext::vulkan::Pipeline::getShaders( uf::stl::vector<ext::vulkan::Shader>& shaders ) {
uf::stl::vector<ext::vulkan::Shader*> ext::vulkan::Pipeline::getShaders( uf::stl::vector<ext::vulkan::Shader>& shaders, const uf::stl::string& type ) {
uf::stl::unordered_map<uf::stl::string, ext::vulkan::Shader*> map;
uf::stl::vector<ext::vulkan::Shader*> res;
bool isCompute = false;
for ( auto& shader : shaders ) {
if ( shader.metadata.pipeline != "" && shader.metadata.pipeline != metadata.type ) continue;
if ( shader.metadata.pipeline != "" && shader.metadata.pipeline != (type == "" ? metadata.type : type) ) continue;
if ( shader.descriptor.stage == VK_SHADER_STAGE_COMPUTE_BIT ) isCompute = true;
}
for ( auto& shader : shaders ) {
if ( shader.metadata.pipeline != "" && shader.metadata.pipeline != metadata.type ) continue;
if ( shader.metadata.pipeline != "" && shader.metadata.pipeline != (type == "" ? metadata.type : type) ) continue;
if ( isCompute && shader.descriptor.stage != VK_SHADER_STAGE_COMPUTE_BIT ) continue;
map[shader.metadata.type] = &shader;
}
for ( auto pair : map ) res.insert( res.begin(), pair.second);
return res;
}
uf::stl::vector<const ext::vulkan::Shader*> ext::vulkan::Pipeline::getShaders( const uf::stl::vector<ext::vulkan::Shader>& shaders ) const {
uf::stl::vector<const ext::vulkan::Shader*> ext::vulkan::Pipeline::getShaders( const uf::stl::vector<ext::vulkan::Shader>& shaders, const uf::stl::string& type ) const {
uf::stl::unordered_map<uf::stl::string, const ext::vulkan::Shader*> map;
uf::stl::vector<const ext::vulkan::Shader*> res;
bool isCompute = false;
for ( auto& shader : shaders ) {
if ( shader.metadata.pipeline != "" && shader.metadata.pipeline != metadata.type ) continue;
if ( shader.metadata.pipeline != "" && shader.metadata.pipeline != (type == "" ? metadata.type : type) ) continue;
if ( shader.descriptor.stage == VK_SHADER_STAGE_COMPUTE_BIT ) isCompute = true;
}
for ( auto& shader : shaders ) {
if ( shader.metadata.pipeline != "" && shader.metadata.pipeline != metadata.type ) continue;
if ( shader.metadata.pipeline != "" && shader.metadata.pipeline != (type == "" ? metadata.type : type) ) continue;
if ( isCompute && shader.descriptor.stage != VK_SHADER_STAGE_COMPUTE_BIT ) continue;
map[shader.metadata.type] = &shader;
}
@ -1839,19 +1846,19 @@ void ext::vulkan::Graphic::record( VkCommandBuffer commandBuffer, size_t pass, s
void ext::vulkan::Graphic::record( VkCommandBuffer commandBuffer, const GraphicDescriptor& descriptor, size_t pass, size_t draw, size_t offset ) const {
if ( !process ) return;
if ( !this->hasPipeline( descriptor ) ) {
VK_DEBUG_VALIDATION_MESSAGE(this << ": has no valid pipeline ({} {})", descriptor.renderMode, descriptor.renderTarget);
//UF_MSG_DEBUG("{} has no valid pipeline ({}:{}:{})", (void*) this, descriptor.renderMode, descriptor.renderTarget, descriptor.pipeline);
return;
}
auto& pipeline = this->getPipeline( descriptor );
if ( pipeline.descriptorSet == VK_NULL_HANDLE ) {
VK_DEBUG_VALIDATION_MESSAGE(this << ": has no valid pipeline descriptor set ({} {})", descriptor.renderMode, descriptor.renderTarget);
//UF_MSG_DEBUG("{} has no valid pipeline descriptor set ({}:{}:{})", (void*) this, descriptor.renderMode, descriptor.renderTarget, descriptor.pipeline);
return;
}
if ( !pipeline.metadata.process ) return;
pipeline.record(*this, descriptor, commandBuffer, pass, draw, offset);
auto shaders = pipeline.getShaders( material.shaders );
auto shaders = pipeline.getShaders( material.shaders, descriptor.pipeline );
for ( auto* shader : shaders ) {
if ( shader->descriptor.stage == VK_SHADER_STAGE_COMPUTE_BIT ) return;
if (

View File

@ -273,8 +273,8 @@ void ext::vulkan::BaseRenderMode::initialize( Device& device ) {
// swapchain.destroy();
swapchain.initialize( device );
// bind swapchain images
images.resize( ext::vulkan::swapchain.buffers );
VK_CHECK_RESULT(vkGetSwapchainImagesKHR( device, swapchain.swapChain, &swapchain.buffers, images.data()));
::images.resize( ext::vulkan::swapchain.buffers );
VK_CHECK_RESULT(vkGetSwapchainImagesKHR( device, swapchain.swapChain, &swapchain.buffers, ::images.data()));
// create image views for swapchain images
renderTarget.attachments.clear();
@ -302,7 +302,7 @@ void ext::vulkan::BaseRenderMode::initialize( Device& device ) {
colorAttachmentView.subresourceRange.layerCount = 1;
colorAttachmentView.viewType = VK_IMAGE_VIEW_TYPE_2D;
colorAttachmentView.flags = 0;
colorAttachmentView.image = images[frame];
colorAttachmentView.image = ::images[frame];
VK_CHECK_RESULT(vkCreateImageView( device, &colorAttachmentView, nullptr, &renderTarget.attachments[frame].view));
VK_REGISTER_HANDLE( renderTarget.attachments[frame].view );
@ -312,7 +312,7 @@ void ext::vulkan::BaseRenderMode::initialize( Device& device ) {
renderTarget.attachments[frame].descriptor.layout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
renderTarget.attachments[frame].descriptor.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
renderTarget.attachments[frame].descriptor.aliased = true;
renderTarget.attachments[frame].image = images[frame];
renderTarget.attachments[frame].image = ::images[frame];
renderTarget.attachments[frame].mem = VK_NULL_HANDLE;
metadata.attachments["color["+std::to_string((int) frame)+"]"] = attachmentIndex++;
@ -530,7 +530,7 @@ void ext::vulkan::BaseRenderMode::initialize( Device& device ) {
// Create framebuffer
{
// Create a frame buffer for every image in the swapchain
renderTarget.framebuffers.resize(images.size());
renderTarget.framebuffers.resize(::images.size());
for (size_t frame = 0; frame < renderTarget.framebuffers.size(); frame++)
{
std::array<VkImageView, 2> attachments;
@ -555,7 +555,7 @@ void ext::vulkan::BaseRenderMode::initialize( Device& device ) {
#if 0
if ( true ) {
auto commandBuffer = device.fetchCommandBuffer(uf::renderer::QueueEnum::TRANSFER);
for ( size_t frame = 0; frame < images.size(); ++frame ) {
for ( size_t frame = 0; frame < ::images.size(); ++frame ) {
VkImageMemoryBarrier imageMemoryBarrier = {};
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
imageMemoryBarrier.srcAccessMask = 0;
@ -652,10 +652,11 @@ void ext::vulkan::BaseRenderMode::destroy() {
}
for ( auto& image : ::images ) {
// vkDestroyImage( *device, image, nullptr );
// vkDestroyImage( *device, image, nullptr ); // destroyed via vkDestroySwapchainKHR
VK_UNREGISTER_HANDLE( image );
image = VK_NULL_HANDLE;
}
::images.clear();
ext::vulkan::RenderMode::destroy();

View File

@ -26,22 +26,34 @@
namespace {
const uf::stl::string DEFERRED_MODE = "compute";
ext::vulkan::Texture depthPyramid;
uf::stl::vector<VkImageView> depthPyramidViews;
void cmdImageBarrier(VkCommandBuffer commandBuffer, VkImage image, VkAccessFlags srcAccess, VkAccessFlags dstAccess, VkImageLayout oldLayout, VkImageLayout newLayout) {
VkImageMemoryBarrier barrier{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};
barrier.srcAccessMask = srcAccess;
barrier.dstAccessMask = dstAccess;
barrier.oldLayout = oldLayout;
barrier.newLayout = newLayout;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = image;
barrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &barrier);
uf::stl::vector<VkImageView> bloomViews;
ext::vulkan::Buffer atomicCounterBloom;
ext::vulkan::Buffer atomicCounterDepth;
struct AtomicCounter {
uint32_t counter;
};
struct PushConstants {
uint32_t mips;
uint32_t numWorkGroups;
uint32_t workGroupOffset;
};
void destroyImageView( ext::vulkan::RenderMode* self, VkImageView view ) {
ext::vulkan::mutex.lock();
auto& texture = self->device->transient.textures.emplace_back();
ext::vulkan::mutex.unlock();
texture.device = self->device;
texture.view = view;
/*
vkDestroyImageView(self->device.logicalDevice, view, nullptr);
VK_UNREGISTER_HANDLE(view);
*/
}
}
#include "./transition.inl"
@ -64,8 +76,7 @@ void ext::vulkan::DeferredRenderMode::initialize( Device& device ) {
struct {
size_t id, bary, depth, uv, normal;
size_t color, bright, motion, scratch, output;
size_t depthPyramid;
size_t color, bright, motion, output;
} attachments = {};
bool blend = true; // !ext::vulkan::settings::invariant::deferredSampling;
@ -108,7 +119,7 @@ void ext::vulkan::DeferredRenderMode::initialize( Device& device ) {
attachments.depth = renderTarget.attach(RenderTarget::Attachment::Descriptor{
/*.format = */ext::vulkan::settings::formats::depth,
/*.layout = */VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
/*.usage = */VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
/*.usage = */VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_STORAGE_BIT,
/*.blend = */false,
/*.samples = */msaa,
//*.mips = */1,
@ -127,13 +138,7 @@ void ext::vulkan::DeferredRenderMode::initialize( Device& device ) {
/*.usage =*/ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
/*.blend =*/ blend,
/*.samples =*/ 1,
});
attachments.scratch = renderTarget.attach(RenderTarget::Attachment::Descriptor{
/*.format =*/ ext::vulkan::settings::pipelines::hdr ? enums::Format::HDR : enums::Format::SDR,
/*.layout = */ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
/*.usage =*/ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
/*.blend =*/ blend,
/*.samples =*/ 1,
/*.mips =*/ mips,
});
attachments.motion = renderTarget.attach(RenderTarget::Attachment::Descriptor{
// /*.format = */VK_FORMAT_R32G32B32A32_SFLOAT,
@ -143,14 +148,6 @@ void ext::vulkan::DeferredRenderMode::initialize( Device& device ) {
/*.blend = */false,
/*.samples = */1,
});
attachments.depthPyramid = renderTarget.attach(RenderTarget::Attachment::Descriptor{
/*.format = */VK_FORMAT_R32_SFLOAT,
/*.layout = */ VK_IMAGE_LAYOUT_GENERAL,
/*.usage = */ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
/*.blend = */false,
/*.samples = */1,
/*.mips = */mips,
});
metadata.attachments["id"] = attachments.id;
@ -164,10 +161,8 @@ void ext::vulkan::DeferredRenderMode::initialize( Device& device ) {
#endif
metadata.attachments["depth"] = attachments.depth;
metadata.attachments["depthPyramid"] = attachments.depthPyramid;
metadata.attachments["color"] = attachments.color;
metadata.attachments["bright"] = attachments.bright;
metadata.attachments["scratch"] = attachments.scratch;
metadata.attachments["motion"] = attachments.motion;
metadata.attachments["output"] = attachments.color;
@ -339,13 +334,67 @@ void ext::vulkan::DeferredRenderMode::initialize( Device& device ) {
}
if ( settings::pipelines::bloom ) {
uf::stl::string computeShaderFilename = uf::io::resolveURI(uf::io::root+"/shaders/display/bloom/comp.spv");
blitter.material.attachShader(computeShaderFilename, uf::renderer::enums::Shader::COMPUTE, "bloom");
uf::stl::string computeShaderFilename = uf::io::resolveURI(uf::io::root+"/shaders/display/bloom/up.comp.spv");
blitter.material.attachShader(computeShaderFilename, uf::renderer::enums::Shader::COMPUTE, "bloom-up");
auto& shader = blitter.material.getShader("compute", "bloom-up");
auto& shader = blitter.material.getShader("compute", "bloom");
shader.aliasAttachment("color", this, VK_IMAGE_LAYOUT_GENERAL);
shader.aliasAttachment("bright", this, VK_IMAGE_LAYOUT_GENERAL);
shader.aliasAttachment("scratch", this, VK_IMAGE_LAYOUT_GENERAL);
}
if ( settings::pipelines::bloom ) {
uf::stl::string computeShaderFilename = uf::io::resolveURI(uf::io::root+"/shaders/display/bloom/down.comp.spv");
blitter.material.attachShader(computeShaderFilename, uf::renderer::enums::Shader::COMPUTE, "bloom-down");
auto& shader = blitter.material.getShader("compute", "bloom-down");
auto mips = uf::vector::mips( pod::Vector2ui{ width, height } );
shader.aliasAttachment("color", this, VK_IMAGE_LAYOUT_GENERAL);
shader.aliasAttachment("bright", this, VK_IMAGE_LAYOUT_GENERAL);
shader.setSpecializationConstants({
{ "MIPS", mips },
});
shader.setDescriptorCounts({
{ "outImage", mips },
});
// atomic counter buffer
::atomicCounterBloom.initialize( (const void*) nullptr, sizeof(::AtomicCounter) * 1, uf::renderer::enums::Buffer::STORAGE );
shader.aliasBuffer("atomicCounterBloom", ::atomicCounterBloom);
for ( auto& view : ::bloomViews ) ::destroyImageView( this, view );
::bloomViews.clear();
::bloomViews.resize(mips);
shader.textures.clear();
ext::vulkan::Texture2D source; source.aliasAttachment( this->getAttachment("bright") );
for ( auto i = 0; i < mips; ++i ) {
auto& view = ::bloomViews[i];
VkImageViewCreateInfo viewCreateInfo = {};
viewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewCreateInfo.pNext = NULL;
viewCreateInfo.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A };
viewCreateInfo.subresourceRange.baseMipLevel = i;
viewCreateInfo.subresourceRange.layerCount = 1;
viewCreateInfo.subresourceRange.levelCount = 1;
viewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
viewCreateInfo.viewType = source.viewType;
viewCreateInfo.format = source.format;
viewCreateInfo.image = source.image;
VK_CHECK_RESULT(vkCreateImageView(device.logicalDevice, &viewCreateInfo, nullptr, &view));
VK_REGISTER_HANDLE(view);
{
auto& texture = shader.textures.emplace_back();
texture.aliasTexture( source );
texture.view = view;
texture.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
texture.updateDescriptors();
}
}
}
if ( settings::pipelines::culling ) {
@ -354,27 +403,31 @@ void ext::vulkan::DeferredRenderMode::initialize( Device& device ) {
auto& shader = blitter.material.getShader("compute", "depth-pyramid");
auto mips = uf::vector::mips( pod::Vector2ui{ width, height } );
// depth pyramid
shader.aliasAttachment("depth", this);
shader.setSpecializationConstants({
{ "MIPS", mips },
});
shader.setDescriptorCounts({
{ "inImage", mips },
{ "outImage", mips },
});
shader.aliasAttachment("depth", this);
// atomic counter buffer
::atomicCounterDepth.initialize( (const void*) nullptr, sizeof(::AtomicCounter) * 1, uf::renderer::enums::Buffer::STORAGE );
shader.aliasBuffer("atomicCounterDepth", ::atomicCounterDepth);
ext::vulkan::Texture2D source; source.aliasAttachment( this->getAttachment("depthPyramid") );
source.sampler.descriptor.reduction.enabled = true;
source.sampler.descriptor.reduction.mode = VK_SAMPLER_REDUCTION_MODE_MIN;
for ( auto& view : ::depthPyramidViews ) {
vkDestroyImageView(device.logicalDevice, view, nullptr);
VK_UNREGISTER_HANDLE(view);
}
for ( auto& view : ::depthPyramidViews ) ::destroyImageView( this, view );
::depthPyramidViews.clear();
::depthPyramidViews.resize(mips);
shader.textures.clear();
storage.buffers.depthPyramid.destroy(true);
storage.buffers.depthPyramid.fromBuffers( NULL, 0, VK_FORMAT_R32_SFLOAT, width, height, 1, 1, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT, VK_IMAGE_LAYOUT_GENERAL );
ext::vulkan::Texture2D& source = storage.buffers.depthPyramid;
source.sampler.descriptor.reduction.enabled = true;
source.sampler.descriptor.reduction.mode = VK_SAMPLER_REDUCTION_MODE_MIN;
for ( auto i = 0; i < mips; ++i ) {
auto& view = ::depthPyramidViews[i];
@ -392,22 +445,14 @@ void ext::vulkan::DeferredRenderMode::initialize( Device& device ) {
VK_CHECK_RESULT(vkCreateImageView(device.logicalDevice, &viewCreateInfo, nullptr, &view));
VK_REGISTER_HANDLE(view);
}
for ( auto i = 0; i < mips; ++i ) {
auto& texture = shader.textures.emplace_back();
texture.aliasTexture( source );
texture.view = ::depthPyramidViews[i];
texture.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
texture.updateDescriptors();
}
for ( auto i = 0; i < mips; ++i ) {
auto& texture = shader.textures.emplace_back();
texture.aliasTexture( source );
texture.view = ::depthPyramidViews[i];
texture.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
texture.updateDescriptors();
{
auto& texture = shader.textures.emplace_back();
texture.aliasTexture( source );
texture.view = view;
texture.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
texture.updateDescriptors();
}
}
}
@ -437,7 +482,18 @@ void ext::vulkan::DeferredRenderMode::initialize( Device& device ) {
}
if ( settings::pipelines::bloom ) {
descriptor.pipeline = "bloom";
descriptor.aux = uf::vector::mips( pod::Vector2ui{ width, height } );
descriptor.pipeline = "bloom-down";
descriptor.subpass = 0;
descriptor.bind.point = VK_PIPELINE_BIND_POINT_COMPUTE;
if ( !blitter.hasPipeline( descriptor ) ) {
blitter.initializePipeline( descriptor );
}
}
if ( settings::pipelines::bloom ) {
descriptor.aux = {};
descriptor.pipeline = "bloom-up";
descriptor.subpass = 0;
descriptor.bind.point = VK_PIPELINE_BIND_POINT_COMPUTE;
if ( !blitter.hasPipeline( descriptor ) ) {
@ -474,6 +530,49 @@ void ext::vulkan::DeferredRenderMode::tick() {
rebuild = true;
renderTarget.initialize( *renderTarget.device );
if ( settings::pipelines::bloom ) {
auto& shader = blitter.material.getShader("compute", "bloom-down");
auto mips = uf::vector::mips( pod::Vector2ui{ width, height } );
shader.setSpecializationConstants({
{ "MIPS", mips },
});
shader.setDescriptorCounts({
{ "outImage", mips },
});
for ( auto& view : ::bloomViews ) ::destroyImageView( this, view );
::bloomViews.clear();
::bloomViews.resize(mips);
shader.textures.clear();
ext::vulkan::Texture2D source; source.aliasAttachment( this->getAttachment("bright") );
for ( auto i = 0; i < mips; ++i ) {
auto& view = ::bloomViews[i];
VkImageViewCreateInfo viewCreateInfo = {};
viewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewCreateInfo.pNext = NULL;
viewCreateInfo.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A };
viewCreateInfo.subresourceRange.baseMipLevel = i;
viewCreateInfo.subresourceRange.layerCount = 1;
viewCreateInfo.subresourceRange.levelCount = 1;
viewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
viewCreateInfo.viewType = source.viewType;
viewCreateInfo.format = source.format;
viewCreateInfo.image = source.image;
VK_CHECK_RESULT(vkCreateImageView(device->logicalDevice, &viewCreateInfo, nullptr, &view));
VK_REGISTER_HANDLE(view);
{
auto& texture = shader.textures.emplace_back();
texture.aliasTexture( source );
texture.view = view;
texture.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
texture.updateDescriptors();
}
}
}
if ( settings::pipelines::culling ) {
auto& shader = blitter.material.getShader("compute", "depth-pyramid");
auto mips = uf::vector::mips( pod::Vector2ui{ width, height } );
@ -481,20 +580,17 @@ void ext::vulkan::DeferredRenderMode::tick() {
{ "MIPS", mips },
});
shader.setDescriptorCounts({
{ "inImage", mips },
{ "outImage", mips },
});
shader.aliasAttachment("depth", this);
storage.buffers.depthPyramid.destroy(true);
storage.buffers.depthPyramid.fromBuffers( NULL, 0, VK_FORMAT_R32_SFLOAT, width, height, 1, 1, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT, VK_IMAGE_LAYOUT_GENERAL );
ext::vulkan::Texture2D source; source.aliasAttachment( this->getAttachment("depthPyramid") );
ext::vulkan::Texture2D& source = storage.buffers.depthPyramid;
source.sampler.descriptor.reduction.enabled = true;
source.sampler.descriptor.reduction.mode = VK_SAMPLER_REDUCTION_MODE_MIN;
for ( auto& view : ::depthPyramidViews ) {
vkDestroyImageView(device->logicalDevice, view, nullptr);
VK_UNREGISTER_HANDLE(view);
}
for ( auto& view : ::depthPyramidViews ) ::destroyImageView( this, view );
::depthPyramidViews.clear();
::depthPyramidViews.resize(mips);
shader.textures.clear();
@ -515,23 +611,14 @@ void ext::vulkan::DeferredRenderMode::tick() {
VK_CHECK_RESULT(vkCreateImageView(device->logicalDevice, &viewCreateInfo, nullptr, &view));
VK_REGISTER_HANDLE(view);
}
for ( auto i = 0; i < mips; ++i ) {
auto& texture = shader.textures.emplace_back();
texture.aliasTexture( source );
texture.view = ::depthPyramidViews[i];
texture.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
texture.updateDescriptors();
}
for ( auto i = 0; i < mips; ++i ) {
auto& texture = shader.textures.emplace_back();
texture.aliasTexture( source );
texture.view = ::depthPyramidViews[i];
texture.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
texture.updateDescriptors();
{
auto& texture = shader.textures.emplace_back();
texture.aliasTexture( source );
texture.view = view;
texture.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
texture.updateDescriptors();
}
}
}
}
@ -582,7 +669,20 @@ void ext::vulkan::DeferredRenderMode::tick() {
}
if ( settings::pipelines::bloom ) {
descriptor.pipeline = "bloom";
descriptor.aux = uf::vector::mips( pod::Vector2ui{ width, height } );
descriptor.pipeline = "bloom-down";
descriptor.subpass = 0;
descriptor.bind.point = VK_PIPELINE_BIND_POINT_COMPUTE;
if ( blitter.hasPipeline( descriptor ) ) {
blitter.getPipeline( descriptor ).update( blitter, descriptor );
} else {
blitter.initializePipeline( descriptor );
}
}
if ( settings::pipelines::bloom ) {
descriptor.aux = {};
descriptor.pipeline = "bloom-up";
descriptor.subpass = 0;
descriptor.bind.point = VK_PIPELINE_BIND_POINT_COMPUTE;
if ( blitter.hasPipeline( descriptor ) ) {
@ -659,6 +759,21 @@ void ext::vulkan::DeferredRenderMode::render() {
//unlockMutex( this->mostRecentCommandPoolId );
}
void ext::vulkan::DeferredRenderMode::destroy() {
// cleanup
::atomicCounterDepth.destroy(false);
::atomicCounterBloom.destroy(false);
for ( auto& view : ::bloomViews ) {
vkDestroyImageView(device->logicalDevice, view, nullptr);
VK_UNREGISTER_HANDLE(view);
}
::bloomViews.clear();
for ( auto& view : ::depthPyramidViews ) {
vkDestroyImageView(device->logicalDevice, view, nullptr);
VK_UNREGISTER_HANDLE(view);
}
::depthPyramidViews.clear();
ext::vulkan::RenderMode::destroy();
}
@ -744,18 +859,6 @@ void ext::vulkan::DeferredRenderMode::createCommandBuffers( const uf::stl::vecto
size_t currentSubpass = 0;
/*
// transition layers for read
for ( auto layer : layers ) {
layer->pipelineBarrier( commandBuffer, 0 );
}
*/
// VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
// VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
// VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
// VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL
#if 1
for ( auto& attachment : renderTarget.attachments ) {
// transition attachments to general attachments for imageStore
@ -863,11 +966,61 @@ void ext::vulkan::DeferredRenderMode::createCommandBuffers( const uf::stl::vecto
::transitionAttachmentsFrom( this, shader, commandBuffer );
}
if ( settings::pipelines::bloom && blitter.material.hasShader("compute", "bloom") ) {
auto& shader = blitter.material.getShader("compute", "bloom");
if ( settings::pipelines::bloom && blitter.material.hasShader("compute", "bloom-down") ) {
auto& shader = blitter.material.getShader("compute", "bloom-down");
auto mips = uf::vector::mips( pod::Vector2ui{ width, height } );
uint32_t dispatchX = (width + 63) / 64;
uint32_t dispatchY = (height + 63) / 64;
uint32_t numWorkGroups = dispatchX * dispatchY;
auto& pushConstant = shader.pushConstants.front().get<::PushConstants>();
pushConstant = {
.mips = mips,
.numWorkGroups = numWorkGroups,
.workGroupOffset = 0,
};
ext::vulkan::GraphicDescriptor descriptor = blitter.descriptor;
descriptor.renderMode = "";
descriptor.pipeline = "bloom";
descriptor.aux = mips;
descriptor.pipeline = "bloom-down";
descriptor.bind.width = dispatchX * 256;
descriptor.bind.height = dispatchY;
descriptor.bind.depth = metadata.eyes;
descriptor.bind.point = VK_PIPELINE_BIND_POINT_COMPUTE;
descriptor.subpass = 0;
// reset counter buffer
vkCmdFillBuffer(commandBuffer, ::atomicCounterBloom.buffer, 0, 4, 0);
VkMemoryBarrier counterBarrier{VK_STRUCTURE_TYPE_MEMORY_BARRIER};
counterBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
counterBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 1, &counterBarrier, 0, nullptr, 0, nullptr);
// transition attachments to general attachments for imageStore
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "setImageLayout" );
::transitionAttachmentsTo( this, shader, commandBuffer );
// dispatch compute shader
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "bloom[down]" );
blitter.record( commandBuffer, descriptor );
/*
ext::vulkan::Texture2D source;
source.aliasAttachment( this->getAttachment("bright") );
source.generateMipmaps( commandBuffer );
*/
// transition attachments back to shader read layouts
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "setImageLayout" );
::transitionAttachmentsFrom( this, shader, commandBuffer );
}
if ( settings::pipelines::bloom && blitter.material.hasShader("compute", "bloom-up") ) {
auto& shader = blitter.material.getShader("compute", "bloom-up");
ext::vulkan::GraphicDescriptor descriptor = blitter.descriptor;
descriptor.renderMode = "";
descriptor.pipeline = "bloom-up";
descriptor.bind.width = width;
descriptor.bind.height = height;
descriptor.bind.depth = metadata.eyes;
@ -879,20 +1032,8 @@ void ext::vulkan::DeferredRenderMode::createCommandBuffers( const uf::stl::vecto
::transitionAttachmentsTo( this, shader, commandBuffer );
// dispatch compute shader
auto& attachmentColor = this->getAttachment("color"); // color
auto& attachmentBright = this->getAttachment("bright"); // bloom
auto& attachmentScratch = this->getAttachment("scratch"); // pingpong
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "bloom[1]" );
blitter.record( commandBuffer, descriptor, 0, 1 );
cmdImageBarrier( commandBuffer, attachmentScratch.image, VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL );
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "bloom[2]" );
blitter.record( commandBuffer, descriptor, 0, 2 );
cmdImageBarrier( commandBuffer, attachmentBright.image, VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL );
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "bloom[3]" );
blitter.record( commandBuffer, descriptor, 0, 3 );
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "bloom[up]" );
blitter.record( commandBuffer, descriptor );
// transition attachments back to shader read layouts
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "setImageLayout" );
@ -900,80 +1041,51 @@ void ext::vulkan::DeferredRenderMode::createCommandBuffers( const uf::stl::vecto
}
// construct depth-pyramid
#if 1
if ( settings::pipelines::culling && blitter.material.hasShader("compute", "depth-pyramid") ) {
auto& shader = blitter.material.getShader("compute", "depth-pyramid");
auto mips = uf::vector::mips( pod::Vector2ui{ width, height } );
uint32_t dispatchX = (width + 63) / 64;
uint32_t dispatchY = (height + 63) / 64;
uint32_t numWorkGroups = dispatchX * dispatchY;
auto& pushConstant = shader.pushConstants.front().get<::PushConstants>();
pushConstant = {
.mips = mips,
.numWorkGroups = numWorkGroups,
.workGroupOffset = 0,
};
ext::vulkan::GraphicDescriptor descriptor = blitter.descriptor;
descriptor.renderMode = "";
descriptor.aux = mips;
descriptor.pipeline = "depth-pyramid";
descriptor.bind.width = dispatchX * 256;
descriptor.bind.height = dispatchY;
descriptor.bind.depth = metadata.eyes;
descriptor.bind.point = VK_PIPELINE_BIND_POINT_COMPUTE;
descriptor.subpass = 0;
// dispatch compute shader
VkMemoryBarrier memoryBarrier{VK_STRUCTURE_TYPE_MEMORY_BARRIER};
memoryBarrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
memoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
// reset counter buffer
vkCmdFillBuffer(commandBuffer, ::atomicCounterDepth.buffer, 0, 4, 0);
VkMemoryBarrier counterBarrier{VK_STRUCTURE_TYPE_MEMORY_BARRIER};
counterBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
counterBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 1, &counterBarrier, 0, nullptr, 0, nullptr);
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "setImageLayout" );
::transitionAttachmentsTo( this, shader, commandBuffer );
for ( auto i = 0; i < mips; ++i ) {
// for some reason it dispatches at half the width without offsetting back...
descriptor.bind.width = std::max(1u, width >> (i - 1));
descriptor.bind.height = std::max(1u, height >> (i - 1));
blitter.record(commandBuffer, descriptor, 0, i);
vkCmdPipelineBarrier( commandBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_FLAGS_NONE, 1, &memoryBarrier, 0, NULL, 0, NULL );
}
blitter.record(commandBuffer, descriptor);
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "setImageLayout" );
::transitionAttachmentsFrom( this, shader, commandBuffer );
}
#endif
// post-renderpass commands
VK_COMMAND_BUFFER_CALLBACK( CALLBACK_END, commandBuffer, frame, {
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "callback[end]" );
} );
#if 0
if ( this->hasAttachment("depth") ) {
auto& attachment = this->getAttachment("depth");
ext::vulkan::Texture texture; texture.aliasAttachment( attachment );
texture.width = width;
texture.height = height;
texture.depth = 1;
texture.imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
texture.descriptor.imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
#if 1
imageMemoryBarrier.subresourceRange.layerCount = metadata.eyes;
imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
uf::renderer::Texture::setImageLayout( commandBuffer, attachment.image, VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, imageMemoryBarrier.subresourceRange );
#endif
for ( size_t eye = 0; eye < metadata.eyes; ++eye ) {
texture.generateMipmaps(commandBuffer, eye);
}
#if 1
uf::renderer::Texture::setImageLayout( commandBuffer, attachment.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL, imageMemoryBarrier.subresourceRange );
imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
imageMemoryBarrier.subresourceRange.layerCount = 1;
#endif
}
#endif
#endif
/*
for ( auto layer : layers ) {
layer->pipelineBarrier( commandBuffer, 1 );
}
*/
}
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::END, "end" );

View File

@ -9,24 +9,41 @@ namespace {
subresourceRange.baseMipLevel = 0;
subresourceRange.levelCount = 1;
subresourceRange.baseArrayLayer = 0;
subresourceRange.layerCount = 1;
subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subresourceRange.layerCount = self->metadata.eyes;
subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
for ( auto& descriptor : shader.metadata.aliases.attachments ) {
if ( descriptor.layout == VK_IMAGE_LAYOUT_UNDEFINED ) continue;
VkImage image = VK_NULL_HANDLE;
VkImageLayout initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
size_t mips = 1;
if ( descriptor.renderMode ) {
if ( descriptor.renderMode->hasAttachment(descriptor.name) )
image = descriptor.renderMode->getAttachment(descriptor.name).image;
if ( descriptor.renderMode->hasAttachment(descriptor.name) ) {
auto& attachment = descriptor.renderMode->getAttachment(descriptor.name);
image = attachment.image;
mips = attachment.descriptor.mips;
initialLayout = attachment.descriptor.layout;
}
} else if ( self->hasAttachment(descriptor.name) ) {
if ( self->hasAttachment(descriptor.name) )
image = self->getAttachment(descriptor.name).image;
if ( self->hasAttachment(descriptor.name) ) {
auto& attachment = self->getAttachment(descriptor.name);
image = attachment.image;
mips = attachment.descriptor.mips;
initialLayout = attachment.descriptor.layout;
}
}
if ( image == VK_NULL_HANDLE ) continue;
subresourceRange.baseMipLevel = 0;
subresourceRange.levelCount = 1;
subresourceRange.aspectMask = descriptor.name == "depth" ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT;
uf::renderer::Texture::setImageLayout( commandBuffer, image, layout, descriptor.layout, subresourceRange );
if ( mips > 1 ) {
subresourceRange.baseMipLevel = 1;
subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
uf::renderer::Texture::setImageLayout( commandBuffer, image, initialLayout, descriptor.layout, subresourceRange );
}
}
}
void transitionAttachmentsFrom(
@ -39,24 +56,41 @@ namespace {
subresourceRange.baseMipLevel = 0;
subresourceRange.levelCount = 1;
subresourceRange.baseArrayLayer = 0;
subresourceRange.layerCount = 1;
subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subresourceRange.layerCount = self->metadata.eyes;
subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
for ( auto& descriptor : shader.metadata.aliases.attachments ) {
if ( descriptor.layout == VK_IMAGE_LAYOUT_UNDEFINED ) continue;
VkImage image = VK_NULL_HANDLE;
VkImageLayout initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
size_t mips = 1;
if ( descriptor.renderMode ) {
if ( descriptor.renderMode->hasAttachment(descriptor.name) )
image = descriptor.renderMode->getAttachment(descriptor.name).image;
if ( descriptor.renderMode->hasAttachment(descriptor.name) ) {
auto& attachment = descriptor.renderMode->getAttachment(descriptor.name);
image = attachment.image;
mips = attachment.descriptor.mips;
initialLayout = attachment.descriptor.layout;
}
} else if ( self->hasAttachment(descriptor.name) ) {
if ( self->hasAttachment(descriptor.name) )
image = self->getAttachment(descriptor.name).image;
if ( self->hasAttachment(descriptor.name) ) {
auto& attachment = self->getAttachment(descriptor.name);
image = attachment.image;
mips = attachment.descriptor.mips;
initialLayout = attachment.descriptor.layout;
}
}
if ( image == VK_NULL_HANDLE ) continue;
subresourceRange.baseMipLevel = 0;
subresourceRange.levelCount = 1;
subresourceRange.aspectMask = descriptor.name == "depth" ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT;
uf::renderer::Texture::setImageLayout( commandBuffer, image, descriptor.layout, layout, subresourceRange );
if ( mips > 1 ) {
subresourceRange.baseMipLevel = 1;
subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
uf::renderer::Texture::setImageLayout( commandBuffer, image, descriptor.layout, initialLayout, subresourceRange );
}
}
}
}

View File

@ -12,8 +12,8 @@
#include <fstream>
#include <regex>
#define VK_DEBUG_VALIDATION_MESSAGE(x)\
// VK_VALIDATION_MESSAGE(x);
#define VK_DEBUG_VALIDATION_MESSAGE(...)\
//VK_VALIDATION_MESSAGE(__VA_ARGS__);
#define UF_SHADER_PARSE_AS_JSON 0
#if UF_SHADER_PARSE_AS_JSON
@ -94,7 +94,7 @@ ext::vulkan::userdata_t ext::vulkan::jsonToUserdata( const ext::json::Value& pay
#if UF_SHADER_TRACK_NAMES
uf::stl::string path = uf::string::join(variableName, ".");
path = uf::string::replace( path, ".[", "[" );
VK_VALIDATION_MESSAGE("[" << (byteBuffer - byteBufferStart) << " / "<< (byteBufferEnd - byteBuffer) <<"]\tInserting: " << path << " = " << value.dump());
//VK_VALIDATION_MESSAGE("[" << (byteBuffer - byteBufferStart) << " / "<< (byteBufferEnd - byteBuffer) <<"]\tInserting: " << path << " = " << value.dump());
#endif
// is strictly an int
if ( value.is<int>(true) ) {
@ -120,7 +120,7 @@ ext::vulkan::userdata_t ext::vulkan::jsonToUserdata( const ext::json::Value& pay
#endif
};
#if UF_SHADER_TRACK_NAMES
VK_VALIDATION_MESSAGE("Updating {} in {}", name, filename);
//VK_VALIDATION_MESSAGE("Updating {} in {}", name, filename);
// VK_VALIDATION_MESSAGE("Iterator: " << (void*) byteBuffer << "\t" << (void*) byteBufferEnd << "\t" << (byteBufferEnd - byteBuffer));
#endif
parse(payload);
@ -264,7 +264,7 @@ ext::vulkan::userdata_t ext::vulkan::jsonToUserdata( const ext::json::Value& pay
#if UF_SHADER_TRACK_NAMES
uf::stl::string path = uf::string::join(variableName, ".");
path = uf::string::replace( path, ".[", "[" );
VK_VALIDATION_MESSAGE("[" << (byteBuffer - byteBufferStart) << " / "<< (byteBufferEnd - byteBuffer) <<"]\tInserting: " << path << " = (" << primitive << ") " << input.dump());
//VK_VALIDATION_MESSAGE("[" << (byteBuffer - byteBufferStart) << " / "<< (byteBufferEnd - byteBuffer) <<"]\tInserting: " << path << " = (" << primitive << ") " << input.dump());
#endif
pushValue( primitive, input );
}
@ -275,12 +275,12 @@ ext::vulkan::userdata_t ext::vulkan::jsonToUserdata( const ext::json::Value& pay
};
auto& definitions = metadata.json["definitions"]["uniforms"][name];
#if UF_SHADER_TRACK_NAMES
VK_VALIDATION_MESSAGE("Updating " << name << " in " << filename);
VK_VALIDATION_MESSAGE("Iterator: " << (void*) byteBuffer << "\t" << (void*) byteBufferEnd << "\t" << (byteBufferEnd - byteBuffer));
//VK_VALIDATION_MESSAGE("Updating " << name << " in " << filename);
//VK_VALIDATION_MESSAGE("Iterator: " << (void*) byteBuffer << "\t" << (void*) byteBufferEnd << "\t" << (byteBufferEnd - byteBuffer));
#endif
parseDefinition(payload, definitions);
#if UF_SHADER_TRACK_NAMES
VK_VALIDATION_MESSAGE("Iterator: " << (void*) byteBuffer << "\t" << (void*) byteBufferEnd << "\t" << (byteBufferEnd - byteBuffer));
//VK_VALIDATION_MESSAGE("Iterator: " << (void*) byteBuffer << "\t" << (void*) byteBufferEnd << "\t" << (byteBufferEnd - byteBuffer));
#endif
#endif
return userdata;
@ -489,14 +489,14 @@ void ext::vulkan::Shader::initialize( ext::vulkan::Device& device, const uf::stl
size_t bufferSize = comp.get_declared_struct_size(base_type);
if ( bufferSize <= 0 ) break;
if ( bufferSize > device.properties.limits.maxUniformBufferRange ) {
VK_DEBUG_VALIDATION_MESSAGE("Invalid uniform buffer length of " << bufferSize << " for shader " << filename);
VK_DEBUG_VALIDATION_MESSAGE("Invalid uniform buffer length of {} for shader {}", bufferSize, filename);
bufferSize = device.properties.limits.maxUniformBufferRange;
}
bufferSize = ALIGNED_SIZE( bufferSize, device.properties.limits.minUniformBufferOffsetAlignment );
{
VK_DEBUG_VALIDATION_MESSAGE("Uniform size of " << bufferSize << " for shader " << filename);
VK_DEBUG_VALIDATION_MESSAGE("Uniform size of {} for shader {}", bufferSize, filename);
// auto& uniform = uniforms.emplace_back();
// uniform.create( bufferSize );
}
@ -564,7 +564,7 @@ void ext::vulkan::Shader::initialize( ext::vulkan::Device& device, const uf::stl
#define LOOP_RESOURCES( key, type ) for ( size_t i = 0; i < res.key.size(); ++i ) {\
const auto& resource = res.key[i];\
VK_DEBUG_VALIDATION_MESSAGE("["<<filename<<"] Found resource: "#type " with binding: " << comp.get_decoration(resource.id, spv::DecorationBinding));\
VK_DEBUG_VALIDATION_MESSAGE("[{}] Found resource: {} with binding: {}", filename, #type, comp.get_decoration(resource.id, spv::DecorationBinding));\
parseResource( resource, type, i );\
}
LOOP_RESOURCES( sampled_images, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER );
@ -667,16 +667,17 @@ void ext::vulkan::Shader::initialize( ext::vulkan::Device& device, const uf::stl
if ( size <= 0 ) continue;
// not a multiple of 4, for some reason
if ( size % 4 != 0 ) {
VK_DEBUG_VALIDATION_MESSAGE("Invalid push constant length of " << size << " for shader " << filename << ", must be multiple of 4, correcting...");
VK_DEBUG_VALIDATION_MESSAGE("Invalid push constant length of {} for shader {}, must be multiple of 4, correcting...", size, filename);
size /= 4;
++size;
size *= 4;
}
if ( size > device.properties.limits.maxPushConstantsSize ) {
VK_DEBUG_VALIDATION_MESSAGE("Invalid push constant length of " << size << " for shader " << filename);
VK_DEBUG_VALIDATION_MESSAGE("Invalid push constant length of {} for shader {}", size, filename);
//VK_DEBUG_VALIDATION_MESSAGE("Invalid push constant length of " << size << " for shader " << filename);
size = device.properties.limits.maxPushConstantsSize;
}
VK_DEBUG_VALIDATION_MESSAGE("Push constant size of " << size << " for shader " << filename);
VK_DEBUG_VALIDATION_MESSAGE("Push constant size of {} for shader {},", size, filename);
{
auto& pushConstant = pushConstants.emplace_back();
pushConstant.create( size );
@ -724,7 +725,7 @@ void ext::vulkan::Shader::initialize( ext::vulkan::Device& device, const uf::stl
specializationMapEntries.emplace_back(specializationMapEntry);
}
specializationConstants.create( specializationSize );
VK_DEBUG_VALIDATION_MESSAGE("Specialization constants size of " << specializationSize << " for shader " << filename);
VK_DEBUG_VALIDATION_MESSAGE("Specialization constants size of {} for shader {}", specializationSize, filename);
uint8_t* s = (uint8_t*) (void*) specializationConstants;
size_t offset = 0;
@ -798,7 +799,7 @@ void ext::vulkan::Shader::initialize( ext::vulkan::Device& device, const uf::stl
definition.validate = false;
} break;
default: {
VK_DEBUG_VALIDATION_MESSAGE("Unregistered specialization constant type at offset " << offset << " for shader " << filename );
VK_DEBUG_VALIDATION_MESSAGE("Unregistered specialization constant type at offset {} for shader {}", offset, filename );
} break;
}
#if UF_SHADER_PARSE_AS_JSON
@ -806,7 +807,7 @@ void ext::vulkan::Shader::initialize( ext::vulkan::Device& device, const uf::stl
member["size"] = size;
member["default"] = member["value"];
metadata.json["specializationConstants"].emplace_back(member);
VK_DEBUG_VALIDATION_MESSAGE("Specialization constant: " << member["type"].as<uf::stl::string>() << " " << name << " = " << member["value"].dump() << "; at offset " << offset << " for shader " << filename );
//VK_DEBUG_VALIDATION_MESSAGE("Specialization constant: " << member["type"].as<uf::stl::string>() << " " << name << " = " << member["value"].dump() << "; at offset " << offset << " for shader " << filename );
#endif
memcpy( &s[offset], &buffer, size );
@ -859,7 +860,7 @@ bool ext::vulkan::Shader::validate() {
if ( it == uniforms.end() ) break;
auto& uniform = *(it++);
if ( uniform.data().len != buffer.allocationInfo.size ) {
VK_DEBUG_VALIDATION_MESSAGE("Uniform size mismatch: Expected " << buffer.allocationInfo.size << ", got " << uniform.data().len << "; fixing...");
VK_DEBUG_VALIDATION_MESSAGE("Uniform size mismatch: Expected {}, got {}; fixing...", buffer.allocationInfo.size, uniform.data().len);
uniform.destroy();
uniform.create(buffer.allocationInfo.size);
valid = false;

View File

@ -205,12 +205,15 @@ void ext::vulkan::Texture::destroy( bool defer ) {
if ( !device || !device->logicalDevice || aliased ) return; // device->logicalDevice should never be null, but it happens, somehow
if ( defer ) {
ext::vulkan::gc::textures.emplace_back( *this );
ext::vulkan::mutex.lock();
device->transient.textures.emplace_back(*this);
ext::vulkan::mutex.unlock();
return;
}
if ( view != VK_NULL_HANDLE ) {
vkDestroyImageView(device->logicalDevice, view, nullptr);
VK_UNREGISTER_HANDLE( view );
view = VK_NULL_HANDLE;
}
if ( image != VK_NULL_HANDLE ) {
@ -581,6 +584,7 @@ void ext::vulkan::Texture::fromBuffers(
viewCreateInfo.subresourceRange.levelCount = this->mips;
viewCreateInfo.image = image;
VK_CHECK_RESULT(vkCreateImageView(device.logicalDevice, &viewCreateInfo, nullptr, &view));
VK_REGISTER_HANDLE( view );
{
auto commandBuffer = device.fetchCommandBuffer(uf::renderer::QueueEnum::GRAPHICS);
@ -890,7 +894,7 @@ void ext::vulkan::Texture::generateMipmaps( VkCommandBuffer commandBuffer, uint3
int32_t mipWidth = width;
int32_t mipHeight = height;
int32_t mipDepth = depth;
int32_t mipDepth = MAX(1, depth);
for ( size_t i = 1; i < this->mips; ++i ) {
// transition previous layer to read from it
barrier.subresourceRange.baseMipLevel = i - 1;
@ -1150,7 +1154,7 @@ uf::Image ext::vulkan::Texture3D::screenshot( uint32_t layerID ) {
imageCopy.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
imageCopy.dstSubresource.baseArrayLayer = 0;
imageCopy.dstSubresource.layerCount = 1;
imageCopy.dstOffset = { 0, 0, 0 };
imageCopy.dstOffset = { 0, 0, layerID };
imageCopy.extent = { this->width, this->height, 1 };
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "copyImage" );

View File

@ -116,8 +116,6 @@ uint32_t ext::vulkan::states::frameAccumulate = 0;
bool ext::vulkan::states::frameAccumulateReset = false;
uint32_t ext::vulkan::states::frameSkip = 0;
uf::stl::vector<ext::vulkan::Texture> ext::vulkan::gc::textures;
uf::ThreadUnique<ext::vulkan::RenderMode*> ext::vulkan::currentRenderMode;
ext::vulkan::Buffer ext::vulkan::scratchBuffer;
@ -495,6 +493,7 @@ void ext::vulkan::initialize( bool soft ) {
void ext::vulkan::tick() {
// ext::vulkan::mutex.lock();
if ( ext::vulkan::states::resized || ext::vulkan::settings::experimental::rebuildOnTickBegin ) {
synchronize(0b11);
ext::vulkan::states::rebuild = true;
::skip = true;
}
@ -534,13 +533,6 @@ void ext::vulkan::tick() {
uf::thread::execute( tasks );
/*
for ( auto& texture : ext::vulkan::gc::textures ) {
texture.destroy( false );
}
ext::vulkan::gc::textures.clear();
*/
if ( ext::vulkan::states::rebuild && ext::vulkan::settings::experimental::skipRenderOnRebuild ) ::skip = true;
ext::vulkan::states::rebuild = false;
@ -666,6 +658,9 @@ void ext::vulkan::render() {
for ( auto& buffer : transient.buffers ) buffer.destroy(false);
transient.buffers.clear();
for ( auto& texture : transient.textures ) texture.destroy(false);
transient.textures.clear();
for ( auto& as : transient.ass ) {
uf::renderer::vkDestroyAccelerationStructureKHR(device, as.handle, nullptr);
VK_UNREGISTER_HANDLE( as.handle );
@ -677,7 +672,7 @@ void ext::vulkan::destroy( bool soft ) {
ext::vulkan::flushCommandBuffers();
// ext::vulkan::mutex.lock();
synchronize();
synchronize(0b11);
#if UF_USE_FFX_FSR
if ( settings::pipelines::fsr ) {
@ -725,7 +720,7 @@ void ext::vulkan::destroy( bool soft ) {
// ext::vulkan::mutex.unlock();
// check for any leaked resources
if ( false ) {
if ( ext::vulkan::settings::validation::checkpoints ) {
UF_MSG_DEBUG("Leaked resources:");
for ( auto& resource : ext::vulkan::Resource<VkBuffer_T*>::handles ) {