Merge pull request #222 from w23/E170

Streams 170-172.
- implements skybox, fixes #140 
- improves material reloading, fixes #207
This commit is contained in:
Ivan Avdeev 2021-11-25 15:36:02 -08:00 committed by GitHub
commit ecaac9d4c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 493 additions and 273 deletions

View File

@ -7,6 +7,7 @@
layout (constant_id = 6) const uint MAX_TEXTURES = 4096; layout (constant_id = 6) const uint MAX_TEXTURES = 4096;
layout (set = 0, binding = 6) uniform sampler2D textures[MAX_TEXTURES]; layout (set = 0, binding = 6) uniform sampler2D textures[MAX_TEXTURES];
layout (set = 0, binding = 13) uniform samplerCube skybox;
layout(location = PAYLOAD_LOCATION_OPAQUE) rayPayloadInEXT RayPayloadOpaque payload; layout(location = PAYLOAD_LOCATION_OPAQUE) rayPayloadInEXT RayPayloadOpaque payload;
@ -79,6 +80,30 @@ void main() {
const uint vi2 = uint(indices[first_index_offset+1]) + kusochki[kusok_index].vertex_offset; const uint vi2 = uint(indices[first_index_offset+1]) + kusochki[kusok_index].vertex_offset;
const uint vi3 = uint(indices[first_index_offset+2]) + kusochki[kusok_index].vertex_offset; const uint vi3 = uint(indices[first_index_offset+2]) + kusochki[kusok_index].vertex_offset;
const vec3 pos[3] = {
gl_ObjectToWorldEXT * vec4(vertices[vi1].pos, 1.),
gl_ObjectToWorldEXT * vec4(vertices[vi2].pos, 1.),
gl_ObjectToWorldEXT * vec4(vertices[vi3].pos, 1.),
};
// This one is supposed to be numerically better, but I can't see why
const vec3 hit_pos = pos[0] * (1. - bary.x - bary.y) + pos[1] * bary.x + pos[2] * bary.y;
//const vec3 hit_pos = gl_WorldRayOriginEXT + gl_WorldRayDirectionEXT * gl_HitTEXT
const uint tex_base_color = kusochki[kusok_index].tex_base_color;
if ((tex_base_color & KUSOK_MATERIAL_FLAG_SKYBOX) != 0) {
payload.hit_pos_t = vec4(hit_pos, gl_HitTEXT);
payload.base_color = vec3(0.);
payload.transmissiveness = 0.;
payload.normal = vec3(0.);
payload.geometry_normal = vec3(0.);
payload.emissive = pow(texture(skybox, gl_WorldRayDirectionEXT).rgb, vec3(2.2)); // Why?! See #230
payload.kusok_index = -1;
payload.roughness = 0.;
payload.metalness = 0.;
payload.material_index = tex_base_color;
return;
}
const vec3 n1 = vertices[vi1].normal; const vec3 n1 = vertices[vi1].normal;
const vec3 n2 = vertices[vi2].normal; const vec3 n2 = vertices[vi2].normal;
const vec3 n3 = vertices[vi3].normal; const vec3 n3 = vertices[vi3].normal;
@ -94,11 +119,6 @@ void main() {
vertices[vi3].gl_tc, vertices[vi3].gl_tc,
}; };
const vec3 pos[3] = {
gl_ObjectToWorldEXT * vec4(vertices[vi1].pos, 1.),
gl_ObjectToWorldEXT * vec4(vertices[vi2].pos, 1.),
gl_ObjectToWorldEXT * vec4(vertices[vi3].pos, 1.),
};
const vec2 texture_uv_stationary = vertices[vi1].gl_tc * (1. - bary.x - bary.y) + vertices[vi2].gl_tc * bary.x + vertices[vi3].gl_tc * bary.y; const vec2 texture_uv_stationary = vertices[vi1].gl_tc * (1. - bary.x - bary.y) + vertices[vi2].gl_tc * bary.x + vertices[vi3].gl_tc * bary.y;
const vec2 texture_uv = texture_uv_stationary + push_constants.time * kusochki[kusok_index].uv_speed; const vec2 texture_uv = texture_uv_stationary + push_constants.time * kusochki[kusok_index].uv_speed;
@ -106,15 +126,11 @@ void main() {
const float geom_normal_sign = sign(dot(real_geom_normal, -gl_WorldRayDirectionEXT)); const float geom_normal_sign = sign(dot(real_geom_normal, -gl_WorldRayDirectionEXT));
const vec3 geom_normal = geom_normal_sign * real_geom_normal; const vec3 geom_normal = geom_normal_sign * real_geom_normal;
// This one is supposed to be numerically better, but I can't see why
const vec3 hit_pos = pos[0] * (1. - bary.x - bary.y) + pos[1] * bary.x + pos[2] * bary.y + geom_normal * normal_offset_fudge;
//const vec3 hit_pos = gl_WorldRayOriginEXT + gl_WorldRayDirectionEXT * gl_HitTEXT + geom_normal * normal_offset_fudge;
const float ray_cone_width = payload.pixel_cone_spread_angle * payload.t_offset; const float ray_cone_width = payload.pixel_cone_spread_angle * payload.t_offset;
vec4 uv_lods; vec4 uv_lods;
computeAnisotropicEllipseAxes(hit_pos, /* TODO geom_?*/ normal, gl_WorldRayDirectionEXT, ray_cone_width, pos, uvs, texture_uv_stationary, uv_lods.xy, uv_lods.zw); computeAnisotropicEllipseAxes(hit_pos, /* TODO geom_?*/ normal, gl_WorldRayDirectionEXT, ray_cone_width, pos, uvs, texture_uv_stationary, uv_lods.xy, uv_lods.zw);
const uint tex_index = kusochki[kusok_index].tex_base_color; const uint tex_index = tex_base_color;
const vec4 tex_color = sampleTexture(tex_index, texture_uv, uv_lods); const vec4 tex_color = sampleTexture(tex_index, texture_uv, uv_lods);
//const vec3 base_color = pow(tex_color.rgb, vec3(2.)); //const vec3 base_color = pow(tex_color.rgb, vec3(2.));
const vec3 base_color = ((push_constants.flags & PUSH_FLAG_LIGHTMAP_ONLY) != 0) ? vec3(1.) : tex_color.rgb;// pow(tex_color.rgb, vec3(2.)); const vec3 base_color = ((push_constants.flags & PUSH_FLAG_LIGHTMAP_ONLY) != 0) ? vec3(1.) : tex_color.rgb;// pow(tex_color.rgb, vec3(2.));
@ -135,7 +151,7 @@ void main() {
// FIXME read alpha from texture // FIXME read alpha from texture
payload.hit_pos_t = vec4(hit_pos, gl_HitTEXT); payload.hit_pos_t = vec4(hit_pos + geom_normal * normal_offset_fudge, gl_HitTEXT);
payload.base_color = base_color * kusochki[kusok_index].color.rgb; payload.base_color = base_color * kusochki[kusok_index].color.rgb;
payload.transmissiveness = (1. - tex_color.a * kusochki[kusok_index].color.a); payload.transmissiveness = (1. - tex_color.a * kusochki[kusok_index].color.a);
payload.normal = normal; payload.normal = normal;

View File

@ -16,6 +16,7 @@ const float throughput_threshold = 1e-3;
layout (constant_id = 4) const float LIGHT_GRID_CELL_SIZE = 256.; layout (constant_id = 4) const float LIGHT_GRID_CELL_SIZE = 256.;
layout (constant_id = 5) const uint MAX_LIGHT_CLUSTERS = 32768; layout (constant_id = 5) const uint MAX_LIGHT_CLUSTERS = 32768;
layout (constant_id = 7) const uint SBT_RECORD_SIZE = 64;
//const uint LIGHT_CLUSTER_SIZE = 2 + MAX_VISIBLE_POINT_LIGHTS + MAX_VISIBLE_SURFACE_LIGHTS; //const uint LIGHT_CLUSTER_SIZE = 2 + MAX_VISIBLE_POINT_LIGHTS + MAX_VISIBLE_SURFACE_LIGHTS;
//const uint LIGHT_CLUSTER_NUM_DLIGHTS_OFFSET = 0; //const uint LIGHT_CLUSTER_NUM_DLIGHTS_OFFSET = 0;
@ -51,7 +52,7 @@ layout(location = PAYLOAD_LOCATION_SHADOW) rayPayloadEXT RayPayloadShadow payloa
layout(location = PAYLOAD_LOCATION_ADDITIVE) rayPayloadEXT RayPayloadAdditive payload_additive; layout(location = PAYLOAD_LOCATION_ADDITIVE) rayPayloadEXT RayPayloadAdditive payload_additive;
bool shadowed(vec3 pos, vec3 dir, float dist) { bool shadowed(vec3 pos, vec3 dir, float dist) {
payload_shadow.shadow = true; payload_shadow.hit_type = SHADOW_HIT;
const uint flags = 0 const uint flags = 0
//| gl_RayFlagsCullFrontFacingTrianglesEXT //| gl_RayFlagsCullFrontFacingTrianglesEXT
//| gl_RayFlagsOpaqueEXT //| gl_RayFlagsOpaqueEXT
@ -63,7 +64,24 @@ bool shadowed(vec3 pos, vec3 dir, float dist) {
GEOMETRY_BIT_OPAQUE, GEOMETRY_BIT_OPAQUE,
0, 0, SHADER_OFFSET_MISS_SHADOW, 0, 0, SHADER_OFFSET_MISS_SHADOW,
pos, 0., dir, dist - shadow_offset_fudge, PAYLOAD_LOCATION_SHADOW); pos, 0., dir, dist - shadow_offset_fudge, PAYLOAD_LOCATION_SHADOW);
return payload_shadow.shadow; return payload_shadow.hit_type == SHADOW_HIT;
}
// TODO join with just shadowed()
bool shadowedSky(vec3 pos, vec3 dir, float dist) {
payload_shadow.hit_type = SHADOW_HIT;
const uint flags = 0
//| gl_RayFlagsCullFrontFacingTrianglesEXT
//| gl_RayFlagsOpaqueEXT
//| gl_RayFlagsTerminateOnFirstHitEXT
//| gl_RayFlagsSkipClosestHitShaderEXT
;
traceRayEXT(tlas,
flags,
GEOMETRY_BIT_OPAQUE,
SHADER_OFFSET_HIT_SHADOW, SBT_RECORD_SIZE, SHADER_OFFSET_MISS_SHADOW,
pos, 0., dir, dist - shadow_offset_fudge, PAYLOAD_LOCATION_SHADOW);
return payload_shadow.hit_type != SHADOW_SKY;
} }
// This is an entry point for evaluation of all other BRDFs based on selected configuration (for direct light) // This is an entry point for evaluation of all other BRDFs based on selected configuration (for direct light)
@ -182,9 +200,9 @@ void computePointLights(uint cluster_index, vec3 throughput, vec3 view_dir, Mate
const float stopdot = lights.point_lights[i].color_stopdot.a; const float stopdot = lights.point_lights[i].color_stopdot.a;
const vec3 dir = lights.point_lights[i].dir_stopdot2.xyz; const vec3 dir = lights.point_lights[i].dir_stopdot2.xyz;
const float stopdot2 = lights.point_lights[i].dir_stopdot2.a; const float stopdot2 = lights.point_lights[i].dir_stopdot2.a;
const bool environment = (lights.point_lights[i].environment == 0); const bool not_environment = (lights.point_lights[i].environment == 0);
const vec3 light_dir = environment ? (origin_r.xyz - payload_opaque.hit_pos_t.xyz) : -dir; // TODO need to randomize sampling direction for environment soft shadow const vec3 light_dir = not_environment ? (origin_r.xyz - payload_opaque.hit_pos_t.xyz) : -dir; // TODO need to randomize sampling direction for environment soft shadow
const vec3 light_dir_norm = normalize(light_dir); const vec3 light_dir_norm = normalize(light_dir);
const float light_dot = dot(light_dir_norm, payload_opaque.normal); const float light_dot = dot(light_dir_norm, payload_opaque.normal);
if (light_dot < 1e-5) if (light_dot < 1e-5)
@ -199,8 +217,8 @@ void computePointLights(uint cluster_index, vec3 throughput, vec3 view_dir, Mate
spot_attenuation = (spot_dot - stopdot2) / (stopdot - stopdot2); spot_attenuation = (spot_dot - stopdot2) / (stopdot - stopdot2);
float fdist = 1.f; float fdist = 1.f;
float light_dist = 1e6; // TODO this is supposedly not the right way to do shadows for environment lights. qrad checks for hitting SURF_SKY, and maybe we should too? float light_dist = 1e4; // TODO this is supposedly not the right way to do shadows for environment lights. qrad checks for hitting SURF_SKY, and maybe we should too?
if (environment) { if (not_environment) {
#if 1 #if 1
const float d2 = dot(light_dir, light_dir); const float d2 = dot(light_dir, light_dir);
const float r2 = origin_r.w * origin_r.w; const float r2 = origin_r.w * origin_r.w;
@ -221,7 +239,6 @@ void computePointLights(uint cluster_index, vec3 throughput, vec3 view_dir, Mate
// if (dot(color,color) < color_culling_threshold) // if (dot(color,color) < color_culling_threshold)
// continue; // continue;
// TODO split into diffuse and specular for denoiser
vec3 ldiffuse, lspecular; vec3 ldiffuse, lspecular;
evalSplitBRDF(payload_opaque.normal, light_dir_norm, view_dir, material, ldiffuse, lspecular); evalSplitBRDF(payload_opaque.normal, light_dir_norm, view_dir, material, ldiffuse, lspecular);
ldiffuse *= color; ldiffuse *= color;
@ -232,9 +249,14 @@ void computePointLights(uint cluster_index, vec3 throughput, vec3 view_dir, Mate
if (dot(combined,combined) < color_culling_threshold) if (dot(combined,combined) < color_culling_threshold)
continue; continue;
// TODO for environment light check that we've hit SURF_SKY if (not_environment) {
if (shadowed(payload_opaque.hit_pos_t.xyz, light_dir_norm, light_dist + shadow_offset_fudge)) if (shadowed(payload_opaque.hit_pos_t.xyz, light_dir_norm, light_dist + shadow_offset_fudge))
continue; continue;
} else {
// for environment light check that we've hit SURF_SKY
if (shadowedSky(payload_opaque.hit_pos_t.xyz, light_dir_norm, light_dist + shadow_offset_fudge))
continue;
}
diffuse += ldiffuse; diffuse += ldiffuse;
specular += lspecular; specular += lspecular;
@ -365,7 +387,7 @@ void main() {
vec3 additive = traceAdditive(origin, direction, payload_opaque.hit_pos_t.w <= 0. ? L : payload_opaque.hit_pos_t.w); vec3 additive = traceAdditive(origin, direction, payload_opaque.hit_pos_t.w <= 0. ? L : payload_opaque.hit_pos_t.w);
// Sky/envmap // Sky/envmap
if (payload_opaque.hit_pos_t.w <= 0.) { if (payload_opaque.kusok_index < 0) {
if (bounce == 0) { if (bounce == 0) {
out_additive += payload_opaque.emissive * color_factor + additive; out_additive += payload_opaque.emissive * color_factor + additive;
} else { } else {

View File

@ -1,14 +1,9 @@
#version 460 core #version 460 core
#extension GL_EXT_nonuniform_qualifier : enable
#extension GL_GOOGLE_include_directive : require #extension GL_GOOGLE_include_directive : require
#include "ray_common.glsl" #include "ray_common.glsl"
#include "ray_kusochki.glsl" #include "ray_kusochki.glsl"
layout (constant_id = 6) const uint MAX_TEXTURES = 4096;
layout (set = 0, binding = 6) uniform sampler2D textures[MAX_TEXTURES];
layout (set = 0, binding = 7/*, align=4*/) uniform UBOLights { Lights lights; };
layout(location = PAYLOAD_LOCATION_OPAQUE) rayPayloadInEXT RayPayloadOpaque payload; layout(location = PAYLOAD_LOCATION_OPAQUE) rayPayloadInEXT RayPayloadOpaque payload;
void main() { void main() {
@ -20,8 +15,4 @@ void main() {
payload.kusok_index = -1; payload.kusok_index = -1;
payload.material_index = 0; payload.material_index = 0;
payload.emissive = vec3(1., 0., 1.); payload.emissive = vec3(1., 0., 1.);
const uint tex_skyplane = lights.skybox_up;
const vec2 sky_uv = gl_WorldRayDirectionEXT.xy * .5 + .5;
payload.emissive = texture(textures[nonuniformEXT(tex_skyplane)], sky_uv).rgb;
} }

View File

@ -19,8 +19,13 @@ struct RayPayloadOpaque {
vec4 debug; vec4 debug;
}; };
#define SHADOW_MISS 0
#define SHADOW_HIT 1
#define SHADOW_SKY 2
struct RayPayloadShadow { struct RayPayloadShadow {
bool shadow; uint hit_type;
}; };

View File

@ -32,6 +32,9 @@ layout (constant_id = 3) const uint MAX_VISIBLE_SURFACE_LIGHTS = 255;
#define SHADER_OFFSET_HIT_REGULAR 0 #define SHADER_OFFSET_HIT_REGULAR 0
#define SHADER_OFFSET_HIT_ALPHA_TEST 1 #define SHADER_OFFSET_HIT_ALPHA_TEST 1
#define SHADER_OFFSET_HIT_ADDITIVE 2 #define SHADER_OFFSET_HIT_ADDITIVE 2
#define SHADER_OFFSET_HIT_SHADOW 3
#define KUSOK_MATERIAL_FLAG_SKYBOX 0x80000000
struct Kusok { struct Kusok {
uint index_offset; uint index_offset;
@ -71,7 +74,7 @@ struct EmissiveKusok {
struct Lights { struct Lights {
uint num_kusochki; uint num_kusochki;
uint num_point_lights; uint num_point_lights;
uint skybox_rt, skybox_bk, skybox_lf, skybox_ft, skybox_up, skybox_dn; PAD(2)
STRUCT EmissiveKusok kusochki[MAX_EMISSIVE_KUSOCHKI]; STRUCT EmissiveKusok kusochki[MAX_EMISSIVE_KUSOCHKI];
STRUCT PointLight point_lights[MAX_POINT_LIGHTS]; STRUCT PointLight point_lights[MAX_POINT_LIGHTS];
}; };

View File

@ -0,0 +1,15 @@
#version 460 core
#extension GL_EXT_ray_tracing: require
#include "ray_kusochki.glsl"
#include "ray_common.glsl"
layout(location = PAYLOAD_LOCATION_SHADOW) rayPayloadInEXT RayPayloadShadow payload_shadow;
void main() {
const int instance_kusochki_offset = gl_InstanceCustomIndexEXT;
const int kusok_index = instance_kusochki_offset + gl_GeometryIndexEXT;
const uint tex_base_color = kusochki[kusok_index].tex_base_color;
payload_shadow.hit_type = ((tex_base_color & KUSOK_MATERIAL_FLAG_SKYBOX) == 0) ? SHADOW_HIT : SHADOW_SKY ;
}

View File

@ -6,5 +6,5 @@
layout(location = PAYLOAD_LOCATION_SHADOW) rayPayloadInEXT RayPayloadShadow payload_shadow; layout(location = PAYLOAD_LOCATION_SHADOW) rayPayloadInEXT RayPayloadShadow payload_shadow;
void main() { void main() {
payload_shadow.shadow = false; payload_shadow.hit_type = SHADOW_MISS;
} }

View File

@ -382,8 +382,9 @@ static qboolean renderableSurface( const msurface_t *surf, int i ) {
return false; return false;
} }
// Explicitly enable SURF_SKY, otherwise they will be skipped by SURF_DRAWTILED
if( FBitSet( surf->flags, SURF_DRAWSKY )) { if( FBitSet( surf->flags, SURF_DRAWSKY )) {
return false; return true;
} }
if( FBitSet( surf->flags, SURF_DRAWTILED )) { if( FBitSet( surf->flags, SURF_DRAWTILED )) {
@ -409,7 +410,6 @@ static model_sizes_t computeSizes( const model_t *mod ) {
const msurface_t *surf = mod->surfaces + mod->firstmodelsurface + i; const msurface_t *surf = mod->surfaces + mod->firstmodelsurface + i;
sizes.water_surfaces += !!(surf->flags & (SURF_DRAWTURB | SURF_DRAWTURB_QUADS)); sizes.water_surfaces += !!(surf->flags & (SURF_DRAWTURB | SURF_DRAWTURB_QUADS));
//sizes.sky_surfaces += !!(surf->flags & SURF_DRAWSKY);
if (!renderableSurface(surf, i)) if (!renderableSurface(surf, i))
continue; continue;
@ -487,7 +487,9 @@ static qboolean loadBrushSurfaces( model_sizes_t sizes, const model_t *mod ) {
model_geometry->material = kXVkMaterialSky; model_geometry->material = kXVkMaterialSky;
} else { } else {
model_geometry->material = kXVkMaterialRegular; model_geometry->material = kXVkMaterialRegular;
VK_CreateSurfaceLightmap( surf, mod ); if (!FBitSet( surf->flags, SURF_DRAWTILED )) {
VK_CreateSurfaceLightmap( surf, mod );
}
} }
if (FBitSet( surf->flags, SURF_CONVEYOR )) { if (FBitSet( surf->flags, SURF_CONVEYOR )) {

60
ref_vk/vk_image.c Normal file
View File

@ -0,0 +1,60 @@
#include "vk_image.h"
xvk_image_t XVK_ImageCreate(const xvk_image_create_t *create) {
xvk_image_t image;
VkMemoryRequirements memreq;
VkImageViewCreateInfo ivci = {.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
VkImageCreateInfo ici = {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.imageType = VK_IMAGE_TYPE_2D,
.extent.width = create->width,
.extent.height = create->height,
.extent.depth = 1,
.mipLevels = create->mips,
.arrayLayers = create->layers,
.format = create->format,
.tiling = create->tiling,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.usage = create->usage,
.samples = VK_SAMPLE_COUNT_1_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.flags = create->is_cubemap ? VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT : 0,
};
XVK_CHECK(vkCreateImage(vk_core.device, &ici, NULL, &image.image));
if (create->debug_name)
SET_DEBUG_NAME(image.image, VK_OBJECT_TYPE_IMAGE, create->debug_name);
vkGetImageMemoryRequirements(vk_core.device, image.image, &memreq);
image.devmem = allocateDeviceMemory(memreq, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0);
XVK_CHECK(vkBindImageMemory(vk_core.device, image.image, image.devmem.device_memory, 0));
ivci.viewType = create->is_cubemap ? VK_IMAGE_VIEW_TYPE_CUBE : VK_IMAGE_VIEW_TYPE_2D;
ivci.format = ici.format;
ivci.image = image.image;
ivci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
ivci.subresourceRange.baseMipLevel = 0;
ivci.subresourceRange.levelCount = ici.mipLevels;
ivci.subresourceRange.baseArrayLayer = 0;
ivci.subresourceRange.layerCount = ici.arrayLayers;
ivci.components = (VkComponentMapping){0, 0, 0, create->has_alpha ? 0 : VK_COMPONENT_SWIZZLE_ONE};
XVK_CHECK(vkCreateImageView(vk_core.device, &ivci, NULL, &image.view));
if (create->debug_name)
SET_DEBUG_NAME(image.view, VK_OBJECT_TYPE_IMAGE_VIEW, create->debug_name);
image.width = create->width;
image.height = create->height;
image.mips = create->mips;
return image;
}
void XVK_ImageDestroy(xvk_image_t *img) {
vkDestroyImageView(vk_core.device, img->view, NULL);
vkDestroyImage(vk_core.device, img->image, NULL);
freeDeviceMemory(&img->devmem);
*img = (xvk_image_t){0};
}

27
ref_vk/vk_image.h Normal file
View File

@ -0,0 +1,27 @@
#pragma once
#include "vk_core.h"
typedef struct {
// FIXME better memory allocation
// OCHEN PLOHO
device_memory_t devmem;
VkImage image;
VkImageView view;
uint32_t width, height;
int mips;
} xvk_image_t;
typedef struct {
const char *debug_name;
uint32_t width, height;
int mips, layers;
VkFormat format;
VkImageTiling tiling;
VkImageUsageFlags usage;
qboolean has_alpha;
qboolean is_cubemap;
} xvk_image_create_t;
xvk_image_t XVK_ImageCreate(const xvk_image_create_t *create);
void XVK_ImageDestroy(xvk_image_t *img);

View File

@ -637,8 +637,8 @@ void VK_LightsAddEmissiveSurface( const struct vk_render_geometry_s *geom, const
if (geom->material != kXVkMaterialSky && geom->material != kXVkMaterialEmissive) { if (geom->material != kXVkMaterialSky && geom->material != kXVkMaterialEmissive) {
VectorCopy(g_lights.map.emissive_textures[texture_num].emissive, esurf->emissive); VectorCopy(g_lights.map.emissive_textures[texture_num].emissive, esurf->emissive);
} else { } else {
// TODO per-map sky emissive // TODO see #227
VectorSet(esurf->emissive, 1000.f, 1000.f, 1000.f); VectorSet(esurf->emissive, 0.f, 0.f, 0.f);
} }
Matrix3x4_Copy(esurf->transform, *transform_row); Matrix3x4_Copy(esurf->transform, *transform_row);

View File

@ -33,17 +33,10 @@ static int findTextureNamedLike( const char *texture_name ) {
return tex_id ? tex_id : -1; return tex_id ? tex_id : -1;
} }
static int loadTextureF( const char *fmt, ... ) {
int tex_id = 0;
char buffer[1024];
va_list argptr;
va_start( argptr, fmt ); static int loadTexture( const char *filename, qboolean force_reload ) {
vsnprintf( buffer, sizeof buffer, fmt, argptr ); const int tex_id = force_reload ? XVK_LoadTextureReplace( filename, NULL, 0, 0 ) : VK_LoadTexture( filename, NULL, 0, 0 );
va_end( argptr ); gEngine.Con_Reportf("Loading texture %s => %d\n", filename, tex_id);
tex_id = VK_LoadTexture( buffer, NULL, 0, 0);
gEngine.Con_Reportf("Loading texture %s => %d\n", buffer, tex_id);
return tex_id ? tex_id : -1; return tex_id ? tex_id : -1;
} }
@ -60,6 +53,9 @@ static void loadMaterialsFromFile( const char *filename ) {
.normalmap = 0, .normalmap = 0,
}; };
int current_material_index = -1; int current_material_index = -1;
qboolean force_reload = false;
gEngine.Con_Reportf("Loading materials from %s\n", filename);
if ( !data ) if ( !data )
return; return;
@ -85,6 +81,7 @@ static void loadMaterialsFromFile( const char *filename ) {
.roughness = tglob.whiteTexture, .roughness = tglob.whiteTexture,
.normalmap = 0, .normalmap = 0,
}; };
force_reload = false;
continue; continue;
} }
@ -104,33 +101,57 @@ static void loadMaterialsFromFile( const char *filename ) {
if (Q_stricmp(key, "for") == 0) { if (Q_stricmp(key, "for") == 0) {
current_material_index = findTextureNamedLike(value); current_material_index = findTextureNamedLike(value);
} else if (Q_stricmp(key, "basecolor_map") == 0) { } else if (Q_stricmp(key, "force_reload") == 0) {
if ((current_material.base_color = loadTextureF("%.*s%s", path_end - path_begin, path_begin, value)) < 0) { force_reload = Q_atoi(value) != 0;
gEngine.Con_Printf(S_ERROR "Failed to load basecolor_map texture %s\n", value);
}
} else if (Q_stricmp(key, "normal_map") == 0) {
if ((current_material.normalmap = loadTextureF("%.*s%s", path_end - path_begin, path_begin, value)) < 0) {
gEngine.Con_Printf(S_ERROR "Failed to load normal_map texture %s\n", value);
current_material.normalmap = 0;
}
} else if (Q_stricmp(key, "metal_map") == 0) {
if ((current_material.metalness = loadTextureF("%.*s%s", path_end - path_begin, path_begin, value)) < 0) {
gEngine.Con_Printf(S_ERROR "Failed to load metal_map texture %s\n", value);
current_material.metalness = tglob.blackTexture;
}
} else if (Q_stricmp(key, "roughness_map") == 0) {
if ((current_material.roughness = loadTextureF("%.*s%s", path_end - path_begin, path_begin, value)) < 0) {
gEngine.Con_Printf(S_ERROR "Failed to load roughness_map texture %s\n", value);
current_material.roughness = tglob.whiteTexture;
}
} else { } else {
gEngine.Con_Printf(S_ERROR "Unknown material key %s\n", key); char texture_path[256];
int *tex_id_dest;
int tex_id = -1;
if (Q_stricmp(key, "basecolor_map") == 0) {
tex_id_dest = &current_material.base_color;
} else if (Q_stricmp(key, "normal_map") == 0) {
tex_id_dest = &current_material.normalmap;
} else if (Q_stricmp(key, "metal_map") == 0) {
tex_id_dest = &current_material.metalness;
} else if (Q_stricmp(key, "roughness_map") == 0) {
tex_id_dest = &current_material.roughness;
} else {
gEngine.Con_Printf(S_ERROR "Unknown material key %s\n", key);
continue;
}
if (value[0] == '/') {
// Path relative to valve/pbr dir
Q_snprintf(texture_path, sizeof(texture_path), "pbr%s", value);
} else {
// Path relative to current material.mat file
Q_snprintf(texture_path, sizeof(texture_path), "%.*s%s", path_end - path_begin, path_begin, value);
}
tex_id = loadTexture(texture_path, force_reload);
if (tex_id < 0) {
gEngine.Con_Printf(S_ERROR "Failed to load texture \"%s\" for key \"%s\"\n", value, key);
continue;
}
*tex_id_dest = tex_id;
} }
} }
Mem_Free( data ); Mem_Free( data );
} }
static void loadMaterialsFromFileF( const char *fmt, ... ) {
char buffer[256];
va_list argptr;
va_start( argptr, fmt );
vsnprintf( buffer, sizeof buffer, fmt, argptr );
va_end( argptr );
loadMaterialsFromFile( buffer );
}
void XVK_ReloadMaterials( void ) { void XVK_ReloadMaterials( void ) {
for (int i = 0; i < MAX_TEXTURES; ++i) { for (int i = 0; i < MAX_TEXTURES; ++i) {
xvk_material_t *const mat = g_materials.materials + i; xvk_material_t *const mat = g_materials.materials + i;
@ -149,7 +170,21 @@ void XVK_ReloadMaterials( void ) {
} }
loadMaterialsFromFile( "pbr/materials.mat" ); loadMaterialsFromFile( "pbr/materials.mat" );
// TODO map-specific loadMaterialsFromFile( "pbr/models/materials.mat" );
{
const char *wad = g_map_entities.wadlist;
for (; *wad;) {
const char *const wad_end = Q_strchr(wad, ';');
loadMaterialsFromFileF("pbr/%.*s/materials.mat", wad_end - wad, wad);
wad = wad_end + 1;
}
}
{
const model_t *map = gEngine.pfnGetModelByIndex( 1 );
loadMaterialsFromFileF("pbr/%s/materials.mat", map->name);
}
} }
xvk_material_t* XVK_GetMaterialForTextureIndex( int tex_index ) { xvk_material_t* XVK_GetMaterialForTextureIndex( int tex_index ) {

View File

@ -136,7 +136,7 @@ void XVK_RayModel_Validate( void ) {
const vk_kusok_data_t *kusok = kusochki + j; const vk_kusok_data_t *kusok = kusochki + j;
const vk_texture_t *tex = findTexture(kusok->tex_base_color); const vk_texture_t *tex = findTexture(kusok->tex_base_color);
ASSERT(tex); ASSERT(tex);
ASSERT(tex->vk.image_view != VK_NULL_HANDLE); ASSERT(tex->vk.image.view != VK_NULL_HANDLE);
// uint32_t index_offset; // uint32_t index_offset;
// uint32_t vertex_offset; // uint32_t vertex_offset;
@ -217,6 +217,12 @@ vk_ray_model_t* VK_RayModelCreate( vk_ray_model_init_t args ) {
kusochki[i].index_offset = mg->index_offset; kusochki[i].index_offset = mg->index_offset;
kusochki[i].triangles = prim_count; kusochki[i].triangles = prim_count;
if (mg->material == kXVkMaterialSky) {
kusochki[i].tex_base_color |= KUSOK_MATERIAL_FLAG_SKYBOX;
} else {
kusochki[i].tex_base_color &= (~KUSOK_MATERIAL_FLAG_SKYBOX);
}
//kusochki[i].texture = mg->texture; //kusochki[i].texture = mg->texture;
//kusochki[i].roughness = mg->material == kXVkMaterialWater ? 0. : 1.; // FIXME //kusochki[i].roughness = mg->material == kXVkMaterialWater ? 0. : 1.; // FIXME
VectorSet(kusochki[i].emissive, 0, 0, 0 ); VectorSet(kusochki[i].emissive, 0, 0, 0 );
@ -379,6 +385,10 @@ void VK_RayFrameAddModel( vk_ray_model_t *model, const vk_render_model_t *render
kusok->tex_roughness = tglob.grayTexture; kusok->tex_roughness = tglob.grayTexture;
} }
if (geom->material == kXVkMaterialSky) {
kusok->tex_base_color |= KUSOK_MATERIAL_FLAG_SKYBOX;
}
Vector4Copy(color, kusok->color); Vector4Copy(color, kusok->color);
XVK_GetEmissiveForTexture( kusok->emissive, geom->texture ); XVK_GetEmissiveForTexture( kusok->emissive, geom->texture );

View File

@ -31,6 +31,8 @@ enum {
ShaderBindingTable_Hit, ShaderBindingTable_Hit,
ShaderBindingTable_Hit_WithAlphaTest, ShaderBindingTable_Hit_WithAlphaTest,
ShaderBindingTable_Hit_Additive, ShaderBindingTable_Hit_Additive,
ShaderBindingTable_Hit_Shadow,
ShaderBindingTable_Hit__END = ShaderBindingTable_Hit_Shadow,
ShaderBindingTable_COUNT ShaderBindingTable_COUNT
}; };
@ -76,16 +78,18 @@ enum {
RayDescBinding_Dest_ImageAdditive = 11, RayDescBinding_Dest_ImageAdditive = 11,
RayDescBinding_Dest_ImageNormals = 12, RayDescBinding_Dest_ImageNormals = 12,
RayDescBinding_SkyboxCube = 13,
RayDescBinding_COUNT RayDescBinding_COUNT
}; };
typedef struct { typedef struct {
vk_image_t denoised; xvk_image_t denoised;
vk_image_t base_color; xvk_image_t base_color;
vk_image_t diffuse_gi; xvk_image_t diffuse_gi;
vk_image_t specular; xvk_image_t specular;
vk_image_t additive; xvk_image_t additive;
vk_image_t normals; xvk_image_t normals;
} xvk_ray_frame_images_t; } xvk_ray_frame_images_t;
static struct { static struct {
@ -351,6 +355,8 @@ static void createPipeline( void )
uint32_t max_visible_surface_lights; uint32_t max_visible_surface_lights;
float light_grid_cell_size; float light_grid_cell_size;
int max_light_clusters; int max_light_clusters;
uint32_t max_textures;
uint32_t sbt_record_size;
} spec_data = { } spec_data = {
.max_point_lights = MAX_POINT_LIGHTS, .max_point_lights = MAX_POINT_LIGHTS,
.max_emissive_kusochki = MAX_EMISSIVE_KUSOCHKI, .max_emissive_kusochki = MAX_EMISSIVE_KUSOCHKI,
@ -358,6 +364,8 @@ static void createPipeline( void )
.max_visible_surface_lights = MAX_VISIBLE_SURFACE_LIGHTS, .max_visible_surface_lights = MAX_VISIBLE_SURFACE_LIGHTS,
.light_grid_cell_size = LIGHT_GRID_CELL_SIZE, .light_grid_cell_size = LIGHT_GRID_CELL_SIZE,
.max_light_clusters = MAX_LIGHT_CLUSTERS, .max_light_clusters = MAX_LIGHT_CLUSTERS,
.max_textures = MAX_TEXTURES,
.sbt_record_size = g_rtx.sbt_record_size,
}; };
const VkSpecializationMapEntry spec_map[] = { const VkSpecializationMapEntry spec_map[] = {
{.constantID = 0, .offset = offsetof(struct RayShaderSpec, max_point_lights), .size = sizeof(int) }, {.constantID = 0, .offset = offsetof(struct RayShaderSpec, max_point_lights), .size = sizeof(int) },
@ -366,6 +374,8 @@ static void createPipeline( void )
{.constantID = 3, .offset = offsetof(struct RayShaderSpec, max_visible_surface_lights), .size = sizeof(uint32_t) }, {.constantID = 3, .offset = offsetof(struct RayShaderSpec, max_visible_surface_lights), .size = sizeof(uint32_t) },
{.constantID = 4, .offset = offsetof(struct RayShaderSpec, light_grid_cell_size), .size = sizeof(float) }, {.constantID = 4, .offset = offsetof(struct RayShaderSpec, light_grid_cell_size), .size = sizeof(float) },
{.constantID = 5, .offset = offsetof(struct RayShaderSpec, max_light_clusters), .size = sizeof(int) }, {.constantID = 5, .offset = offsetof(struct RayShaderSpec, max_light_clusters), .size = sizeof(int) },
{.constantID = 6, .offset = offsetof(struct RayShaderSpec, max_textures), .size = sizeof(uint32_t) },
{.constantID = 7, .offset = offsetof(struct RayShaderSpec, sbt_record_size), .size = sizeof(uint32_t) },
}; };
VkSpecializationInfo spec = { VkSpecializationInfo spec = {
@ -381,6 +391,7 @@ static void createPipeline( void )
ShaderStageIndex_Miss_Shadow, ShaderStageIndex_Miss_Shadow,
ShaderStageIndex_Miss_Empty, ShaderStageIndex_Miss_Empty,
ShaderStageIndex_ClosestHit, ShaderStageIndex_ClosestHit,
ShaderStageIndex_ClosestHit_Shadow,
ShaderStageIndex_AnyHit_AlphaTest, ShaderStageIndex_AnyHit_AlphaTest,
ShaderStageIndex_AnyHit_Additive, ShaderStageIndex_AnyHit_Additive,
ShaderStageIndex_COUNT, ShaderStageIndex_COUNT,
@ -414,6 +425,7 @@ static void createPipeline( void )
DEFINE_SHADER("shadow.rmiss.spv", MISS, ShaderStageIndex_Miss_Shadow); DEFINE_SHADER("shadow.rmiss.spv", MISS, ShaderStageIndex_Miss_Shadow);
DEFINE_SHADER("empty.rmiss.spv", MISS, ShaderStageIndex_Miss_Empty); DEFINE_SHADER("empty.rmiss.spv", MISS, ShaderStageIndex_Miss_Empty);
DEFINE_SHADER("ray.rchit.spv", CLOSEST_HIT, ShaderStageIndex_ClosestHit); DEFINE_SHADER("ray.rchit.spv", CLOSEST_HIT, ShaderStageIndex_ClosestHit);
DEFINE_SHADER("shadow.rchit.spv", CLOSEST_HIT, ShaderStageIndex_ClosestHit_Shadow);
DEFINE_SHADER("alphamask.rahit.spv", ANY_HIT, ShaderStageIndex_AnyHit_AlphaTest); DEFINE_SHADER("alphamask.rahit.spv", ANY_HIT, ShaderStageIndex_AnyHit_AlphaTest);
DEFINE_SHADER("additive.rahit.spv", ANY_HIT, ShaderStageIndex_AnyHit_Additive); DEFINE_SHADER("additive.rahit.spv", ANY_HIT, ShaderStageIndex_AnyHit_Additive);
@ -427,6 +439,7 @@ static void createPipeline( void )
ASSERT_SHADER_OFFSET(ShaderBindingTable_Hit, ShaderBindingTable_Hit, SHADER_OFFSET_HIT_REGULAR); ASSERT_SHADER_OFFSET(ShaderBindingTable_Hit, ShaderBindingTable_Hit, SHADER_OFFSET_HIT_REGULAR);
ASSERT_SHADER_OFFSET(ShaderBindingTable_Hit, ShaderBindingTable_Hit_WithAlphaTest, SHADER_OFFSET_HIT_ALPHA_TEST); ASSERT_SHADER_OFFSET(ShaderBindingTable_Hit, ShaderBindingTable_Hit_WithAlphaTest, SHADER_OFFSET_HIT_ALPHA_TEST);
ASSERT_SHADER_OFFSET(ShaderBindingTable_Hit, ShaderBindingTable_Hit_Additive, SHADER_OFFSET_HIT_ADDITIVE); ASSERT_SHADER_OFFSET(ShaderBindingTable_Hit, ShaderBindingTable_Hit_Additive, SHADER_OFFSET_HIT_ADDITIVE);
ASSERT_SHADER_OFFSET(ShaderBindingTable_Hit, ShaderBindingTable_Hit_Shadow, SHADER_OFFSET_HIT_SHADOW);
shader_groups[ShaderBindingTable_RayGen] = (VkRayTracingShaderGroupCreateInfoKHR) { shader_groups[ShaderBindingTable_RayGen] = (VkRayTracingShaderGroupCreateInfoKHR) {
.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR, .sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR,
@ -491,6 +504,15 @@ static void createPipeline( void )
.intersectionShader = VK_SHADER_UNUSED_KHR, .intersectionShader = VK_SHADER_UNUSED_KHR,
}; };
shader_groups[ShaderBindingTable_Hit_Shadow] = (VkRayTracingShaderGroupCreateInfoKHR) {
.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR,
.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_KHR,
.anyHitShader = VK_SHADER_UNUSED_KHR,
.closestHitShader = ShaderStageIndex_ClosestHit_Shadow,
.generalShader = VK_SHADER_UNUSED_KHR,
.intersectionShader = VK_SHADER_UNUSED_KHR,
};
XVK_CHECK(vkCreateRayTracingPipelinesKHR(vk_core.device, VK_NULL_HANDLE, g_pipeline_cache, 1, &rtpci, NULL, &g_rtx.pipeline)); XVK_CHECK(vkCreateRayTracingPipelinesKHR(vk_core.device, VK_NULL_HANDLE, g_pipeline_cache, 1, &rtpci, NULL, &g_rtx.pipeline));
ASSERT(g_rtx.pipeline != VK_NULL_HANDLE); ASSERT(g_rtx.pipeline != VK_NULL_HANDLE);
@ -613,12 +635,18 @@ static void updateDescriptors( VkCommandBuffer cmdbuf, const vk_ray_frame_render
g_rtx.desc_values[RayDescBinding_Textures].image_array = dii_all_textures; g_rtx.desc_values[RayDescBinding_Textures].image_array = dii_all_textures;
g_rtx.desc_values[RayDescBinding_SkyboxCube].image = (VkDescriptorImageInfo){
.sampler = vk_core.default_sampler,
.imageView = tglob.skybox_cube.vk.image.view ? tglob.skybox_cube.vk.image.view : tglob.cubemap_placeholder.vk.image.view,
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
};
// TODO: move this to vk_texture.c // TODO: move this to vk_texture.c
for (int i = 0; i < MAX_TEXTURES; ++i) { for (int i = 0; i < MAX_TEXTURES; ++i) {
const vk_texture_t *texture = findTexture(i); const vk_texture_t *texture = findTexture(i);
const qboolean exists = texture->vk.image_view != VK_NULL_HANDLE; const qboolean exists = texture->vk.image.view != VK_NULL_HANDLE;
dii_all_textures[i].sampler = vk_core.default_sampler; // FIXME on AMD using pImmutableSamplers leads to NEAREST filtering ??. VK_NULL_HANDLE; dii_all_textures[i].sampler = vk_core.default_sampler; // FIXME on AMD using pImmutableSamplers leads to NEAREST filtering ??. VK_NULL_HANDLE;
dii_all_textures[i].imageView = exists ? texture->vk.image_view : findTexture(tglob.defaultTexture)->vk.image_view; dii_all_textures[i].imageView = exists ? texture->vk.image.view : findTexture(tglob.defaultTexture)->vk.image.view;
ASSERT(dii_all_textures[i].imageView != VK_NULL_HANDLE); ASSERT(dii_all_textures[i].imageView != VK_NULL_HANDLE);
dii_all_textures[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; dii_all_textures[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
} }
@ -731,7 +759,7 @@ LIST_GBUFFER_IMAGES(GBUFFER_WRITE_BARRIER)
} }
const VkStridedDeviceAddressRegionKHR sbt_raygen = SBT_INDEX(ShaderBindingTable_RayGen, 1); const VkStridedDeviceAddressRegionKHR sbt_raygen = SBT_INDEX(ShaderBindingTable_RayGen, 1);
const VkStridedDeviceAddressRegionKHR sbt_miss = SBT_INDEX(ShaderBindingTable_Miss, ShaderBindingTable_Miss_Empty - ShaderBindingTable_Miss); const VkStridedDeviceAddressRegionKHR sbt_miss = SBT_INDEX(ShaderBindingTable_Miss, ShaderBindingTable_Miss_Empty - ShaderBindingTable_Miss);
const VkStridedDeviceAddressRegionKHR sbt_hit = SBT_INDEX(ShaderBindingTable_Hit, ShaderBindingTable_Hit_Additive - ShaderBindingTable_Hit); const VkStridedDeviceAddressRegionKHR sbt_hit = SBT_INDEX(ShaderBindingTable_Hit, ShaderBindingTable_Hit__END - ShaderBindingTable_Hit);
const VkStridedDeviceAddressRegionKHR sbt_callable = { 0 }; const VkStridedDeviceAddressRegionKHR sbt_callable = { 0 };
vkCmdTraceRaysKHR(cmdbuf, &sbt_raygen, &sbt_miss, &sbt_hit, &sbt_callable, FRAME_WIDTH, FRAME_HEIGHT, 1 ); vkCmdTraceRaysKHR(cmdbuf, &sbt_raygen, &sbt_miss, &sbt_hit, &sbt_callable, FRAME_WIDTH, FRAME_HEIGHT, 1 );
@ -793,13 +821,6 @@ static void updateLights( void )
dst->environment = !!(src->flags & LightFlag_Environment); dst->environment = !!(src->flags & LightFlag_Environment);
} }
lights->skybox_rt = tglob.skyboxTextures[0];
lights->skybox_bk = tglob.skyboxTextures[1];
lights->skybox_lf = tglob.skyboxTextures[2];
lights->skybox_ft = tglob.skyboxTextures[3];
lights->skybox_up = tglob.skyboxTextures[4];
lights->skybox_dn = tglob.skyboxTextures[5];
} }
} }
@ -1148,6 +1169,16 @@ static void createLayouts( void ) {
.pImmutableSamplers = NULL, //samplers, .pImmutableSamplers = NULL, //samplers,
}; };
g_rtx.desc_bindings[RayDescBinding_SkyboxCube] = (VkDescriptorSetLayoutBinding){
.binding = RayDescBinding_SkyboxCube,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR | VK_SHADER_STAGE_MISS_BIT_KHR,
// FIXME on AMD using immutable samplers leads to nearest filtering ???!
.pImmutableSamplers = NULL, //samplers,
};
// for (int i = 0; i < ARRAYSIZE(samplers); ++i) // for (int i = 0; i < ARRAYSIZE(samplers); ++i)
// samplers[i] = vk_core.default_sampler; // samplers[i] = vk_core.default_sampler;
@ -1248,30 +1279,33 @@ qboolean VK_RayInit( void )
createPipeline(); createPipeline();
for (int i = 0; i < ARRAYSIZE(g_rtx.frames); ++i) { for (int i = 0; i < ARRAYSIZE(g_rtx.frames); ++i) {
g_rtx.frames[i].denoised = VK_ImageCreate(FRAME_WIDTH, FRAME_HEIGHT, VK_FORMAT_R16G16B16A16_SFLOAT, #define CREATE_GBUFFER_IMAGE(name, format_, add_usage_bits) \
VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT ); do { \
SET_DEBUG_NAMEF(g_rtx.frames[i].denoised.image, VK_OBJECT_TYPE_IMAGE, "rtx frames[%d] denoised", i); char debug_name[64]; \
const xvk_image_create_t create = { \
g_rtx.frames[i].base_color = VK_ImageCreate(FRAME_WIDTH, FRAME_HEIGHT, VK_FORMAT_R8G8B8A8_UNORM, .debug_name = debug_name, \
VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_STORAGE_BIT); .width = FRAME_WIDTH, \
SET_DEBUG_NAMEF(g_rtx.frames[i].base_color.image, VK_OBJECT_TYPE_IMAGE, "rtx frames[%d] base_color", i); .height = FRAME_HEIGHT, \
.mips = 1, \
g_rtx.frames[i].diffuse_gi = VK_ImageCreate(FRAME_WIDTH, FRAME_HEIGHT, VK_FORMAT_R16G16B16A16_SFLOAT, .layers = 1, \
VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_STORAGE_BIT); .format = format_, \
SET_DEBUG_NAMEF(g_rtx.frames[i].diffuse_gi.image, VK_OBJECT_TYPE_IMAGE, "rtx frames[%d] diffuse_gi", i); .tiling = VK_IMAGE_TILING_OPTIMAL, \
.usage = VK_IMAGE_USAGE_STORAGE_BIT | add_usage_bits, \
g_rtx.frames[i].specular = VK_ImageCreate(FRAME_WIDTH, FRAME_HEIGHT, VK_FORMAT_R16G16B16A16_SFLOAT, .has_alpha = true, \
VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_STORAGE_BIT); .is_cubemap = false, \
SET_DEBUG_NAMEF(g_rtx.frames[i].specular.image, VK_OBJECT_TYPE_IMAGE, "rtx frames[%d] specular", i); }; \
Q_snprintf(debug_name, sizeof(debug_name), "rtx frames[%d] " # name, i); \
g_rtx.frames[i].additive = VK_ImageCreate(FRAME_WIDTH, FRAME_HEIGHT, VK_FORMAT_R16G16B16A16_SFLOAT, g_rtx.frames[i].name = XVK_ImageCreate(&create); \
VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_STORAGE_BIT); } while(0)
SET_DEBUG_NAMEF(g_rtx.frames[i].additive.image, VK_OBJECT_TYPE_IMAGE, "rtx frames[%d] additive", i);
CREATE_GBUFFER_IMAGE(denoised, VK_FORMAT_R16G16B16A16_SFLOAT, VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
CREATE_GBUFFER_IMAGE(base_color, VK_FORMAT_R8G8B8A8_UNORM, 0);
CREATE_GBUFFER_IMAGE(diffuse_gi, VK_FORMAT_R16G16B16A16_SFLOAT, 0);
CREATE_GBUFFER_IMAGE(specular, VK_FORMAT_R16G16B16A16_SFLOAT, 0);
CREATE_GBUFFER_IMAGE(additive, VK_FORMAT_R16G16B16A16_SFLOAT, 0);
// TODO make sure this format and usage is suppported // TODO make sure this format and usage is suppported
g_rtx.frames[i].normals = VK_ImageCreate(FRAME_WIDTH, FRAME_HEIGHT, VK_FORMAT_R16G16B16A16_SNORM, CREATE_GBUFFER_IMAGE(normals, VK_FORMAT_R16G16B16A16_SNORM, 0);
VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_STORAGE_BIT); #undef CREATE_GBUFFER_IMAGE
SET_DEBUG_NAMEF(g_rtx.frames[i].normals.image, VK_OBJECT_TYPE_IMAGE, "rtx frames[%d] normals", i);
} }
if (vk_core.debug) { if (vk_core.debug) {
@ -1287,12 +1321,12 @@ void VK_RayShutdown( void ) {
ASSERT(vk_core.rtx); ASSERT(vk_core.rtx);
for (int i = 0; i < ARRAYSIZE(g_rtx.frames); ++i) { for (int i = 0; i < ARRAYSIZE(g_rtx.frames); ++i) {
VK_ImageDestroy(&g_rtx.frames[i].denoised); XVK_ImageDestroy(&g_rtx.frames[i].denoised);
VK_ImageDestroy(&g_rtx.frames[i].base_color); XVK_ImageDestroy(&g_rtx.frames[i].base_color);
VK_ImageDestroy(&g_rtx.frames[i].diffuse_gi); XVK_ImageDestroy(&g_rtx.frames[i].diffuse_gi);
VK_ImageDestroy(&g_rtx.frames[i].specular); XVK_ImageDestroy(&g_rtx.frames[i].specular);
VK_ImageDestroy(&g_rtx.frames[i].additive); XVK_ImageDestroy(&g_rtx.frames[i].additive);
VK_ImageDestroy(&g_rtx.frames[i].normals); XVK_ImageDestroy(&g_rtx.frames[i].normals);
} }
vkDestroyPipeline(vk_core.device, g_rtx.pipeline, NULL); vkDestroyPipeline(vk_core.device, g_rtx.pipeline, NULL);

View File

@ -48,11 +48,18 @@ void initTextures( void )
*/ */
} }
static void unloadSkybox( void );
void destroyTextures( void ) void destroyTextures( void )
{ {
for( unsigned int i = 0; i < vk_numTextures; i++ ) for( unsigned int i = 0; i < vk_numTextures; i++ )
VK_FreeTexture( i ); VK_FreeTexture( i );
unloadSkybox();
XVK_ImageDestroy(&tglob.cubemap_placeholder.vk.image);
memset(&tglob.cubemap_placeholder, 0, sizeof(tglob.cubemap_placeholder));
//memset( tglob.lightmapTextures, 0, sizeof( tglob.lightmapTextures )); //memset( tglob.lightmapTextures, 0, sizeof( tglob.lightmapTextures ));
memset( vk_texturesHashTable, 0, sizeof( vk_texturesHashTable )); memset( vk_texturesHashTable, 0, sizeof( vk_texturesHashTable ));
memset( vk_textures, 0, sizeof( vk_textures )); memset( vk_textures, 0, sizeof( vk_textures ));
@ -230,6 +237,8 @@ static void VK_ProcessImage( vk_texture_t *tex, rgbdata_t *pic )
} }
} }
static qboolean uploadTexture(vk_texture_t *tex, rgbdata_t *const *const layers, int num_layers, qboolean cubemap);
static void VK_CreateInternalTextures( void ) static void VK_CreateInternalTextures( void )
{ {
int dx2, dy, d; int dx2, dy, d;
@ -290,6 +299,22 @@ static void VK_CreateInternalTextures( void )
// cinematic dummy // cinematic dummy
pic = Common_FakeImage( 640, 100, 1, IMAGE_HAS_COLOR ); pic = Common_FakeImage( 640, 100, 1, IMAGE_HAS_COLOR );
tglob.cinTexture = VK_LoadTextureInternal( "*cintexture", pic, TF_NOMIPMAP|TF_CLAMP ); tglob.cinTexture = VK_LoadTextureInternal( "*cintexture", pic, TF_NOMIPMAP|TF_CLAMP );
{
rgbdata_t *sides[6];
pic = Common_FakeImage( 4, 4, 1, IMAGE_HAS_COLOR );
for( x = 0; x < 16; x++ )
((uint *)pic->buffer)[x] = 0xFFFFFFFF;
sides[0] = pic;
sides[1] = pic;
sides[2] = pic;
sides[3] = pic;
sides[4] = pic;
sides[5] = pic;
uploadTexture( &tglob.cubemap_placeholder, sides, 6, true );
}
} }
static VkFormat VK_GetFormat(pixformat_t format) static VkFormat VK_GetFormat(pixformat_t format)
@ -445,63 +470,69 @@ static void BuildMipMap( byte *in, int srcWidth, int srcHeight, int srcDepth, in
} }
} }
static qboolean VK_UploadTexture(vk_texture_t *tex, rgbdata_t *pic) static qboolean uploadTexture(vk_texture_t *tex, rgbdata_t *const *const layers, int num_layers, qboolean cubemap) {
{ const VkFormat format = VK_GetFormat(layers[0]->type);
const VkFormat format = VK_GetFormat(pic->type);
const VkImageTiling tiling = VK_IMAGE_TILING_OPTIMAL;
const VkImageUsageFlags usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
byte *buf = pic->buffer;
int mipCount = 0; int mipCount = 0;
// TODO non-rbga textures // TODO non-rbga textures
// TODO cubemaps
if (!pic->buffer) for (int i = 0; i < num_layers; ++i) {
return false; if (!layers[i]->buffer) {
gEngine.Con_Printf(S_ERROR "Texture %s layer %d missing buffer\n", tex->name, i);
return false;
}
tex->width = pic->width; if (i == 0)
tex->height = pic->height; continue;
mipCount = CalcMipmapCount( tex, ( buf != NULL ));
gEngine.Con_Reportf("Uploading texture %s, mips=%d\n", tex->name, mipCount); if (layers[0]->type != layers[i]->type) {
gEngine.Con_Printf(S_ERROR "Texture %s layer %d has type %d inconsistent with layer 0 type %d\n", tex->name, i, layers[i]->type, layers[0]->type);
return false;
}
if (layers[0]->width != layers[i]->width || layers[0]->height != layers[i]->height) {
gEngine.Con_Printf(S_ERROR "Texture %s layer %d has resolution %dx%d inconsistent with layer 0 resolution %dx%d\n",
tex->name, i, layers[i]->width, layers[i]->height, layers[0]->width, layers[0]->height);
return false;
}
if ((layers[0]->flags ^ layers[i]->flags) & IMAGE_HAS_ALPHA) {
gEngine.Con_Printf(S_ERROR "Texture %s layer %d has_alpha=%d inconsistent with layer 0 has_alpha=%d\n",
tex->name, i,
!!(layers[i]->flags & IMAGE_HAS_ALPHA),
!!(layers[0]->flags & IMAGE_HAS_ALPHA));
}
}
tex->width = layers[0]->width;
tex->height = layers[0]->height;
mipCount = CalcMipmapCount( tex, true);
gEngine.Con_Reportf("Uploading texture %s, mips=%d, layers=%d\n", tex->name, mipCount, num_layers);
// TODO this vvv // TODO this vvv
// // NOTE: only single uncompressed textures can be resamples, no mips, no layers, no sides // // NOTE: only single uncompressed textures can be resamples, no mips, no layers, no sides
// if(( tex->depth == 1 ) && (( pic->width != tex->width ) || ( pic->height != tex->height ))) // if(( tex->depth == 1 ) && (( layers->width != tex->width ) || ( layers->height != tex->height )))
// data = GL_ResampleTexture( buf, pic->width, pic->height, tex->width, tex->height, normalMap ); // data = GL_ResampleTexture( buf, layers->width, layers->height, tex->width, tex->height, normalMap );
// else data = buf; // else data = buf;
// if( !ImageDXT( pic->type ) && !FBitSet( tex->flags, TF_NOMIPMAP ) && FBitSet( pic->flags, IMAGE_ONEBIT_ALPHA )) // if( !ImageDXT( layers->type ) && !FBitSet( tex->flags, TF_NOMIPMAP ) && FBitSet( layers->flags, IMAGE_ONEBIT_ALPHA ))
// data = GL_ApplyFilter( data, tex->width, tex->height ); // data = GL_ApplyFilter( data, tex->width, tex->height );
// 1. Create VkImage w/ usage = DST|SAMPLED, layout=UNDEFINED
{ {
VkImageCreateInfo image_create_info = { const xvk_image_create_t create = {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .debug_name = tex->name,
.imageType = VK_IMAGE_TYPE_2D, .width = tex->width,
.extent.width = pic->width, .height = tex->height,
.extent.height = pic->height, .mips = mipCount,
.extent.depth = 1, .layers = num_layers,
.format = format, .format = format,
.mipLevels = mipCount, .tiling = VK_IMAGE_TILING_OPTIMAL,
.arrayLayers = 1, .usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
.tiling = tiling, .has_alpha = layers[0]->flags & IMAGE_HAS_ALPHA,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, .is_cubemap = cubemap,
.usage = usage,
.samples = VK_SAMPLE_COUNT_1_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
}; };
XVK_CHECK(vkCreateImage(vk_core.device, &image_create_info, NULL, &tex->vk.image)); tex->vk.image = XVK_ImageCreate(&create);
SET_DEBUG_NAME(tex->vk.image, VK_OBJECT_TYPE_IMAGE, tex->name);
}
// 2. Alloc mem for VkImage and bind it (DEV_LOCAL)
{
VkMemoryRequirements memreq;
vkGetImageMemoryRequirements(vk_core.device, tex->vk.image, &memreq);
tex->vk.device_memory = allocateDeviceMemory(memreq, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0);
XVK_CHECK(vkBindImageMemory(vk_core.device, tex->vk.image, tex->vk.device_memory.device_memory, tex->vk.device_memory.offset));
} }
{ {
@ -517,7 +548,7 @@ static qboolean VK_UploadTexture(vk_texture_t *tex, rgbdata_t *pic)
// 5.1.1 transitionToLayout(UNDEFINED -> DST) // 5.1.1 transitionToLayout(UNDEFINED -> DST)
VkImageMemoryBarrier image_barrier = { VkImageMemoryBarrier image_barrier = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.image = tex->vk.image, .image = tex->vk.image.image,
.srcAccessMask = 0, .srcAccessMask = 0,
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
@ -527,7 +558,7 @@ static qboolean VK_UploadTexture(vk_texture_t *tex, rgbdata_t *pic)
.baseMipLevel = 0, .baseMipLevel = 0,
.levelCount = mipCount, .levelCount = mipCount,
.baseArrayLayer = 0, .baseArrayLayer = 0,
.layerCount = 1, .layerCount = num_layers,
}}; }};
XVK_CHECK(vkBeginCommandBuffer(vk_core.cb_tex, &beginfo)); XVK_CHECK(vkBeginCommandBuffer(vk_core.cb_tex, &beginfo));
@ -537,9 +568,10 @@ static qboolean VK_UploadTexture(vk_texture_t *tex, rgbdata_t *pic)
0, 0, NULL, 0, NULL, 1, &image_barrier); 0, 0, NULL, 0, NULL, 1, &image_barrier);
// 5.1.2 copyBufferToImage for all mip levels // 5.1.2 copyBufferToImage for all mip levels
{ for (int layer = 0; layer < num_layers; ++layer) {
for (int mip = 0; mip < mipCount; ++mip) for (int mip = 0; mip < mipCount; ++mip) {
{ const rgbdata_t *const pic = layers[layer];
byte *buf = pic->buffer;
const int width = Q_max( 1, ( pic->width >> mip )); const int width = Q_max( 1, ( pic->width >> mip ));
const int height = Q_max( 1, ( pic->height >> mip )); const int height = Q_max( 1, ( pic->height >> mip ));
const size_t mip_size = CalcImageSize( pic->type, width, height, 1 ); const size_t mip_size = CalcImageSize( pic->type, width, height, 1 );
@ -551,7 +583,7 @@ static qboolean VK_UploadTexture(vk_texture_t *tex, rgbdata_t *pic)
region.imageSubresource = (VkImageSubresourceLayers){ region.imageSubresource = (VkImageSubresourceLayers){
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = mip, .mipLevel = mip,
.baseArrayLayer = 0, .baseArrayLayer = layer,
.layerCount = 1, .layerCount = 1,
}; };
region.imageExtent = (VkExtent3D){ region.imageExtent = (VkExtent3D){
@ -568,7 +600,7 @@ static qboolean VK_UploadTexture(vk_texture_t *tex, rgbdata_t *pic)
} }
// TODO we could do this only once w/ region array // TODO we could do this only once w/ region array
vkCmdCopyBufferToImage(vk_core.cb_tex, vk_core.staging.buffer, tex->vk.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region); vkCmdCopyBufferToImage(vk_core.cb_tex, vk_core.staging.buffer, tex->vk.image.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
staging_offset += mip_size; staging_offset += mip_size;
} }
@ -585,7 +617,7 @@ static qboolean VK_UploadTexture(vk_texture_t *tex, rgbdata_t *pic)
.baseMipLevel = 0, .baseMipLevel = 0,
.levelCount = mipCount, .levelCount = mipCount,
.baseArrayLayer = 0, .baseArrayLayer = 0,
.layerCount = 1, .layerCount = num_layers,
}; };
vkCmdPipelineBarrier(vk_core.cb_tex, vkCmdPipelineBarrier(vk_core.cb_tex,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
@ -603,28 +635,13 @@ static qboolean VK_UploadTexture(vk_texture_t *tex, rgbdata_t *pic)
XVK_CHECK(vkQueueWaitIdle(vk_core.queue)); XVK_CHECK(vkQueueWaitIdle(vk_core.queue));
} }
{
VkImageViewCreateInfo ivci = {.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
ivci.viewType = VK_IMAGE_VIEW_TYPE_2D;
ivci.format = format;
ivci.image = tex->vk.image;
ivci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
ivci.subresourceRange.baseMipLevel = 0;
ivci.subresourceRange.levelCount = mipCount;
ivci.subresourceRange.baseArrayLayer = 0;
ivci.subresourceRange.layerCount = 1;
ivci.components = (VkComponentMapping){0, 0, 0, (pic->flags & IMAGE_HAS_ALPHA) ? 0 : VK_COMPONENT_SWIZZLE_ONE};
XVK_CHECK(vkCreateImageView(vk_core.device, &ivci, NULL, &tex->vk.image_view));
SET_DEBUG_NAME(tex->vk.image_view, VK_OBJECT_TYPE_IMAGE_VIEW, tex->name);
}
// TODO how should we approach this: // TODO how should we approach this:
// - per-texture desc sets can be inconvenient if texture is used in different incompatible contexts // - per-texture desc sets can be inconvenient if texture is used in different incompatible contexts
// - update descriptor sets in batch? // - update descriptor sets in batch?
if (vk_desc.next_free != MAX_TEXTURES) if (vk_desc.next_free != MAX_TEXTURES)
{ {
VkDescriptorImageInfo dii_tex = { VkDescriptorImageInfo dii_tex = {
.imageView = tex->vk.image_view, .imageView = tex->vk.image.view,
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
}; };
VkWriteDescriptorSet wds[] = { { VkWriteDescriptorSet wds[] = { {
@ -707,7 +724,7 @@ int VK_LoadTexture( const char *name, const byte *buf, size_t size, int flags )
// upload texture // upload texture
VK_ProcessImage( tex, pic ); VK_ProcessImage( tex, pic );
if( !VK_UploadTexture( tex, pic )) if( !uploadTexture( tex, &pic, 1, false ))
{ {
memset( tex, 0, sizeof( vk_texture_t )); memset( tex, 0, sizeof( vk_texture_t ));
gEngine.FS_FreeImage( pic ); // release source texture gEngine.FS_FreeImage( pic ); // release source texture
@ -727,6 +744,19 @@ int VK_LoadTexture( const char *name, const byte *buf, size_t size, int flags )
return tex - vk_textures; return tex - vk_textures;
} }
int XVK_LoadTextureReplace( const char *name, const byte *buf, size_t size, int flags ) {
vk_texture_t *tex;
if( !Common_CheckTexName( name ))
return 0;
// free if already loaded
if(( tex = Common_TextureForName( name ))) {
VK_FreeTexture( tex - vk_textures );
}
return VK_LoadTexture( name, buf, size, flags );
}
int VK_CreateTexture( const char *name, int width, int height, const void *buffer, texFlags_t flags ) int VK_CreateTexture( const char *name, int width, int height, const void *buffer, texFlags_t flags )
{ {
gEngine.Con_Printf("VK FIXME: %s\n", __FUNCTION__); gEngine.Con_Printf("VK FIXME: %s\n", __FUNCTION__);
@ -756,7 +786,7 @@ void VK_FreeTexture( unsigned int texnum ) {
ASSERT( tex != NULL ); ASSERT( tex != NULL );
// already freed? // already freed?
if( !tex->vk.image ) return; if( !tex->vk.image.image ) return;
// debug // debug
if( !tex->name[0] ) if( !tex->name[0] )
@ -787,9 +817,7 @@ void VK_FreeTexture( unsigned int texnum ) {
gEngine.FS_FreeImage( tex->original ); gEngine.FS_FreeImage( tex->original );
*/ */
vkDestroyImageView(vk_core.device, tex->vk.image_view, NULL); XVK_ImageDestroy(&tex->vk.image);
vkDestroyImage(vk_core.device, tex->vk.image, NULL);
freeDeviceMemory(&tex->vk.device_memory);
memset(tex, 0, sizeof(*tex)); memset(tex, 0, sizeof(*tex));
} }
@ -821,7 +849,7 @@ int VK_LoadTextureFromBuffer( const char *name, rgbdata_t *pic, texFlags_t flags
VK_ProcessImage( tex, pic ); VK_ProcessImage( tex, pic );
if( !VK_UploadTexture( tex, pic )) if( !uploadTexture( tex, &pic, 1, false ))
{ {
memset( tex, 0, sizeof( vk_texture_t )); memset( tex, 0, sizeof( vk_texture_t ));
return 0; return 0;
@ -834,51 +862,6 @@ int VK_LoadTextureFromBuffer( const char *name, rgbdata_t *pic, texFlags_t flags
return (tex - vk_textures); return (tex - vk_textures);
} }
vk_image_t VK_ImageCreate(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage) {
vk_image_t image;
VkMemoryRequirements memreq;
VkImageViewCreateInfo ivci = {.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
VkImageCreateInfo ici = {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.imageType = VK_IMAGE_TYPE_2D,
.extent.width = width,
.extent.height = height,
.extent.depth = 1,
.mipLevels = 1,
.arrayLayers = 1,
.format = format,
.tiling = tiling,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.usage = usage,
.samples = VK_SAMPLE_COUNT_1_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
};
XVK_CHECK(vkCreateImage(vk_core.device, &ici, NULL, &image.image));
vkGetImageMemoryRequirements(vk_core.device, image.image, &memreq);
image.devmem = allocateDeviceMemory(memreq, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0);
XVK_CHECK(vkBindImageMemory(vk_core.device, image.image, image.devmem.device_memory, 0));
ivci.viewType = VK_IMAGE_VIEW_TYPE_2D;
ivci.format = ici.format;
ivci.image = image.image;
ivci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
ivci.subresourceRange.levelCount = 1;
ivci.subresourceRange.layerCount = 1;
XVK_CHECK(vkCreateImageView(vk_core.device, &ivci, NULL, &image.view));
return image;
}
void VK_ImageDestroy(vk_image_t *img) {
vkDestroyImageView(vk_core.device, img->view, NULL);
vkDestroyImage(vk_core.device, img->image, NULL);
freeDeviceMemory(&img->devmem);
*img = (vk_image_t){0};
}
int XVK_TextureLookupF( const char *fmt, ...) { int XVK_TextureLookupF( const char *fmt, ...) {
int tex_id = 0; int tex_id = 0;
char buffer[1024]; char buffer[1024];
@ -892,25 +875,26 @@ int XVK_TextureLookupF( const char *fmt, ...) {
return tex_id; return tex_id;
} }
static void unloadSkybox( void ) static void unloadSkybox( void ) {
{ if (tglob.skybox_cube.vk.image.image) {
int i; XVK_ImageDestroy(&tglob.skybox_cube.vk.image);
memset(&tglob.skybox_cube, 0, sizeof(tglob.skybox_cube));
// release old skybox
for( i = 0; i < 6; i++ )
{
if( !tglob.skyboxTextures[i] ) continue;
VK_FreeTexture( tglob.skyboxTextures[i] );
} }
tglob.skyboxbasenum = 5800; // set skybox base (to let some mods load hi-res skyboxes)
memset( tglob.skyboxTextures, 0, sizeof( tglob.skyboxTextures ));
tglob.fCustomSkybox = false; tglob.fCustomSkybox = false;
} }
static const char* r_skyBoxSuffix[6] = { "rt", "bk", "lf", "ft", "up", "dn" }; static struct {
//static const int r_skyTexOrder[6] = { 0, 2, 1, 3, 4, 5 }; const char *suffix;
uint flags;
} g_skybox_info[6] = {
{"rt", IMAGE_ROT_90},
{"lf", IMAGE_FLIP_Y | IMAGE_ROT_90 | IMAGE_FLIP_X},
{"bk", IMAGE_FLIP_Y},
{"ft", IMAGE_FLIP_X},
{"up", IMAGE_ROT_90},
{"dn", IMAGE_ROT_90},
};
#define SKYBOX_MISSED 0 #define SKYBOX_MISSED 0
#define SKYBOX_HLSTYLE 1 #define SKYBOX_HLSTYLE 1
@ -929,7 +913,7 @@ static int CheckSkybox( const char *name )
for( j = 0; j < 6; j++ ) for( j = 0; j < 6; j++ )
{ {
// build side name // build side name
sidename = va( "%s%s.%s", name, r_skyBoxSuffix[j], skybox_ext[i] ); sidename = va( "%s%s.%s", name, g_skybox_info[j].suffix, skybox_ext[i] );
if( gEngine.FS_FileExists( sidename, false )) if( gEngine.FS_FileExists( sidename, false ))
num_checked_sides++; num_checked_sides++;
@ -941,7 +925,7 @@ static int CheckSkybox( const char *name )
for( j = 0; j < 6; j++ ) for( j = 0; j < 6; j++ )
{ {
// build side name // build side name
sidename = va( "%s_%s.%s", name, r_skyBoxSuffix[j], skybox_ext[i] ); sidename = va( "%s_%s.%s", name, g_skybox_info[j].suffix, skybox_ext[i] );
if( gEngine.FS_FileExists( sidename, false )) if( gEngine.FS_FileExists( sidename, false ))
num_checked_sides++; num_checked_sides++;
} }
@ -958,6 +942,8 @@ void XVK_SetupSky( const char *skyboxname )
char loadname[MAX_STRING]; char loadname[MAX_STRING];
char sidename[MAX_STRING]; char sidename[MAX_STRING];
int i, result, len; int i, result, len;
rgbdata_t *sides[6];
qboolean success = false;
if( !COM_CheckString( skyboxname )) if( !COM_CheckString( skyboxname ))
{ {
@ -990,21 +976,43 @@ void XVK_SetupSky( const char *skyboxname )
for( i = 0; i < 6; i++ ) for( i = 0; i < 6; i++ )
{ {
if( result == SKYBOX_HLSTYLE ) if( result == SKYBOX_HLSTYLE )
Q_snprintf( sidename, sizeof( sidename ), "%s%s", loadname, r_skyBoxSuffix[i] ); Q_snprintf( sidename, sizeof( sidename ), "%s%s", loadname, g_skybox_info[i].suffix );
else Q_snprintf( sidename, sizeof( sidename ), "%s_%s", loadname, r_skyBoxSuffix[i] ); else Q_snprintf( sidename, sizeof( sidename ), "%s_%s", loadname, g_skybox_info[i].suffix );
tglob.skyboxTextures[i] = VK_LoadTexture( sidename, NULL, 0, TF_CLAMP|TF_SKY ); sides[i] = gEngine.FS_LoadImage( sidename, NULL, 0);
if( !tglob.skyboxTextures[i] ) break; if (!sides[i] || !sides[i]->buffer)
gEngine.Con_DPrintf( "%s%s%s", skyboxname, r_skyBoxSuffix[i], i != 5 ? ", " : ". " ); break;
{
uint img_flags = g_skybox_info[i].flags;
// we need to expand image into RGBA buffer
if( sides[i]->type == PF_INDEXED_24 || sides[i]->type == PF_INDEXED_32 )
img_flags |= IMAGE_FORCE_RGBA;
gEngine.Image_Process( &sides[i], 0, 0, img_flags, 0.f );
}
gEngine.Con_DPrintf( "%s%s%s", skyboxname, g_skybox_info[i].suffix, i != 5 ? ", " : ". " );
} }
if( i == 6 ) if( i != 6 )
{ goto cleanup;
if( !Common_CheckTexName( loadname ))
goto cleanup;
Q_strncpy( tglob.skybox_cube.name, loadname, sizeof( tglob.skybox_cube.name ));
success = uploadTexture(&tglob.skybox_cube, sides, 6, true);
cleanup:
for (int j = 0; j < i; ++j)
gEngine.FS_FreeImage( sides[j] ); // release source texture
if (success) {
tglob.fCustomSkybox = true; tglob.fCustomSkybox = true;
gEngine.Con_DPrintf( "done\n" ); gEngine.Con_DPrintf( "done\n" );
return; // loaded } else {
tglob.skybox_cube.name[0] = '\0';
gEngine.Con_DPrintf( "^2failed\n" );
unloadSkybox();
} }
gEngine.Con_DPrintf( "^2failed\n" );
unloadSkybox();
} }

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "vk_core.h" #include "vk_core.h"
#include "vk_image.h"
#include "xash3d_types.h" #include "xash3d_types.h"
#include "const.h" #include "const.h"
@ -14,9 +15,7 @@ typedef struct vk_texture_s
uint texnum; uint texnum;
struct { struct {
VkImage image; xvk_image_t image;
VkImageView image_view;
device_memory_t device_memory;
VkDescriptorSet descriptor; VkDescriptorSet descriptor;
} vk; } vk;
@ -37,13 +36,15 @@ typedef struct vk_textures_global_s
int alphaskyTexture; // quake1 alpha-sky layer int alphaskyTexture; // quake1 alpha-sky layer
int lightmapTextures[MAX_LIGHTMAPS]; int lightmapTextures[MAX_LIGHTMAPS];
int dlightTexture; // custom dlight texture int dlightTexture; // custom dlight texture
int skyboxTextures[6]; // skybox sides
int cinTexture; // cinematic texture int cinTexture; // cinematic texture
int skytexturenum; // this not a gl_texturenum! int skytexturenum; // this not a gl_texturenum!
int skyboxbasenum; // start with 5800 FIXME remove this, lewa says this is a GL1 hack int skyboxbasenum; // start with 5800 FIXME remove this, lewa says this is a GL1 hack
qboolean fCustomSkybox; // TODO do we need this for anything? qboolean fCustomSkybox; // TODO do we need this for anything?
vk_texture_t skybox_cube;
vk_texture_t cubemap_placeholder;
} vk_textures_global_t; } vk_textures_global_t;
// TODO rename this consistently // TODO rename this consistently
@ -65,19 +66,10 @@ int VK_CreateTextureArray( const char *name, int width, int height, int depth,
void VK_FreeTexture( unsigned int texnum ); void VK_FreeTexture( unsigned int texnum );
int VK_LoadTextureFromBuffer( const char *name, rgbdata_t *pic, texFlags_t flags, qboolean update ); int VK_LoadTextureFromBuffer( const char *name, rgbdata_t *pic, texFlags_t flags, qboolean update );
int XVK_LoadTextureReplace( const char *name, const byte *buf, size_t size, int flags );
int XVK_TextureLookupF( const char *fmt, ...); int XVK_TextureLookupF( const char *fmt, ...);
#define VK_LoadTextureInternal( name, pic, flags ) VK_LoadTextureFromBuffer( name, pic, flags, false ) #define VK_LoadTextureInternal( name, pic, flags ) VK_LoadTextureFromBuffer( name, pic, flags, false )
void XVK_SetupSky( const char *skyboxname ); void XVK_SetupSky( const char *skyboxname );
typedef struct {
// FIXME better memory allocation
// OCHEN PLOHO
device_memory_t devmem;
VkImage image;
VkImageView view;
} vk_image_t;
vk_image_t VK_ImageCreate(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage);
void VK_ImageDestroy(vk_image_t *img);