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 (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;
@ -79,6 +80,30 @@ void main() {
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 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 n2 = vertices[vi2].normal;
const vec3 n3 = vertices[vi3].normal;
@ -94,11 +119,6 @@ void main() {
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 = 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 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;
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);
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 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.));
@ -135,7 +151,7 @@ void main() {
// 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.transmissiveness = (1. - tex_color.a * kusochki[kusok_index].color.a);
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 = 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_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;
bool shadowed(vec3 pos, vec3 dir, float dist) {
payload_shadow.shadow = true;
payload_shadow.hit_type = SHADOW_HIT;
const uint flags = 0
//| gl_RayFlagsCullFrontFacingTrianglesEXT
//| gl_RayFlagsOpaqueEXT
@ -63,7 +64,24 @@ bool shadowed(vec3 pos, vec3 dir, float dist) {
GEOMETRY_BIT_OPAQUE,
0, 0, SHADER_OFFSET_MISS_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)
@ -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 vec3 dir = lights.point_lights[i].dir_stopdot2.xyz;
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 float light_dot = dot(light_dir_norm, payload_opaque.normal);
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);
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?
if (environment) {
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 (not_environment) {
#if 1
const float d2 = dot(light_dir, light_dir);
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)
// continue;
// TODO split into diffuse and specular for denoiser
vec3 ldiffuse, lspecular;
evalSplitBRDF(payload_opaque.normal, light_dir_norm, view_dir, material, ldiffuse, lspecular);
ldiffuse *= color;
@ -232,9 +249,14 @@ void computePointLights(uint cluster_index, vec3 throughput, vec3 view_dir, Mate
if (dot(combined,combined) < color_culling_threshold)
continue;
// TODO for environment light check that we've hit SURF_SKY
if (shadowed(payload_opaque.hit_pos_t.xyz, light_dir_norm, light_dist + shadow_offset_fudge))
continue;
if (not_environment) {
if (shadowed(payload_opaque.hit_pos_t.xyz, light_dir_norm, light_dist + shadow_offset_fudge))
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;
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);
// Sky/envmap
if (payload_opaque.hit_pos_t.w <= 0.) {
if (payload_opaque.kusok_index < 0) {
if (bounce == 0) {
out_additive += payload_opaque.emissive * color_factor + additive;
} else {

View File

@ -1,14 +1,9 @@
#version 460 core
#extension GL_EXT_nonuniform_qualifier : enable
#extension GL_GOOGLE_include_directive : require
#include "ray_common.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;
void main() {
@ -20,8 +15,4 @@ void main() {
payload.kusok_index = -1;
payload.material_index = 0;
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;
};
#define SHADOW_MISS 0
#define SHADOW_HIT 1
#define SHADOW_SKY 2
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_ALPHA_TEST 1
#define SHADER_OFFSET_HIT_ADDITIVE 2
#define SHADER_OFFSET_HIT_SHADOW 3
#define KUSOK_MATERIAL_FLAG_SKYBOX 0x80000000
struct Kusok {
uint index_offset;
@ -71,7 +74,7 @@ struct EmissiveKusok {
struct Lights {
uint num_kusochki;
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 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;
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;
}
// Explicitly enable SURF_SKY, otherwise they will be skipped by SURF_DRAWTILED
if( FBitSet( surf->flags, SURF_DRAWSKY )) {
return false;
return true;
}
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;
sizes.water_surfaces += !!(surf->flags & (SURF_DRAWTURB | SURF_DRAWTURB_QUADS));
//sizes.sky_surfaces += !!(surf->flags & SURF_DRAWSKY);
if (!renderableSurface(surf, i))
continue;
@ -487,7 +487,9 @@ static qboolean loadBrushSurfaces( model_sizes_t sizes, const model_t *mod ) {
model_geometry->material = kXVkMaterialSky;
} else {
model_geometry->material = kXVkMaterialRegular;
VK_CreateSurfaceLightmap( surf, mod );
if (!FBitSet( surf->flags, SURF_DRAWTILED )) {
VK_CreateSurfaceLightmap( surf, mod );
}
}
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) {
VectorCopy(g_lights.map.emissive_textures[texture_num].emissive, esurf->emissive);
} else {
// TODO per-map sky emissive
VectorSet(esurf->emissive, 1000.f, 1000.f, 1000.f);
// TODO see #227
VectorSet(esurf->emissive, 0.f, 0.f, 0.f);
}
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;
}
static int loadTextureF( const char *fmt, ... ) {
int tex_id = 0;
char buffer[1024];
va_list argptr;
va_start( argptr, fmt );
vsnprintf( buffer, sizeof buffer, fmt, argptr );
va_end( argptr );
tex_id = VK_LoadTexture( buffer, NULL, 0, 0);
gEngine.Con_Reportf("Loading texture %s => %d\n", buffer, tex_id);
static int loadTexture( const char *filename, qboolean force_reload ) {
const int tex_id = force_reload ? XVK_LoadTextureReplace( filename, NULL, 0, 0 ) : VK_LoadTexture( filename, NULL, 0, 0 );
gEngine.Con_Reportf("Loading texture %s => %d\n", filename, tex_id);
return tex_id ? tex_id : -1;
}
@ -60,6 +53,9 @@ static void loadMaterialsFromFile( const char *filename ) {
.normalmap = 0,
};
int current_material_index = -1;
qboolean force_reload = false;
gEngine.Con_Reportf("Loading materials from %s\n", filename);
if ( !data )
return;
@ -85,6 +81,7 @@ static void loadMaterialsFromFile( const char *filename ) {
.roughness = tglob.whiteTexture,
.normalmap = 0,
};
force_reload = false;
continue;
}
@ -104,33 +101,57 @@ static void loadMaterialsFromFile( const char *filename ) {
if (Q_stricmp(key, "for") == 0) {
current_material_index = findTextureNamedLike(value);
} else if (Q_stricmp(key, "basecolor_map") == 0) {
if ((current_material.base_color = loadTextureF("%.*s%s", path_end - path_begin, path_begin, 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 if (Q_stricmp(key, "force_reload") == 0) {
force_reload = Q_atoi(value) != 0;
} 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 );
}
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 ) {
for (int i = 0; i < MAX_TEXTURES; ++i) {
xvk_material_t *const mat = g_materials.materials + i;
@ -149,7 +170,21 @@ void XVK_ReloadMaterials( void ) {
}
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 ) {

View File

@ -136,7 +136,7 @@ void XVK_RayModel_Validate( void ) {
const vk_kusok_data_t *kusok = kusochki + j;
const vk_texture_t *tex = findTexture(kusok->tex_base_color);
ASSERT(tex);
ASSERT(tex->vk.image_view != VK_NULL_HANDLE);
ASSERT(tex->vk.image.view != VK_NULL_HANDLE);
// uint32_t index_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].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].roughness = mg->material == kXVkMaterialWater ? 0. : 1.; // FIXME
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;
}
if (geom->material == kXVkMaterialSky) {
kusok->tex_base_color |= KUSOK_MATERIAL_FLAG_SKYBOX;
}
Vector4Copy(color, kusok->color);
XVK_GetEmissiveForTexture( kusok->emissive, geom->texture );

View File

@ -31,6 +31,8 @@ enum {
ShaderBindingTable_Hit,
ShaderBindingTable_Hit_WithAlphaTest,
ShaderBindingTable_Hit_Additive,
ShaderBindingTable_Hit_Shadow,
ShaderBindingTable_Hit__END = ShaderBindingTable_Hit_Shadow,
ShaderBindingTable_COUNT
};
@ -76,16 +78,18 @@ enum {
RayDescBinding_Dest_ImageAdditive = 11,
RayDescBinding_Dest_ImageNormals = 12,
RayDescBinding_SkyboxCube = 13,
RayDescBinding_COUNT
};
typedef struct {
vk_image_t denoised;
vk_image_t base_color;
vk_image_t diffuse_gi;
vk_image_t specular;
vk_image_t additive;
vk_image_t normals;
xvk_image_t denoised;
xvk_image_t base_color;
xvk_image_t diffuse_gi;
xvk_image_t specular;
xvk_image_t additive;
xvk_image_t normals;
} xvk_ray_frame_images_t;
static struct {
@ -351,6 +355,8 @@ static void createPipeline( void )
uint32_t max_visible_surface_lights;
float light_grid_cell_size;
int max_light_clusters;
uint32_t max_textures;
uint32_t sbt_record_size;
} spec_data = {
.max_point_lights = MAX_POINT_LIGHTS,
.max_emissive_kusochki = MAX_EMISSIVE_KUSOCHKI,
@ -358,6 +364,8 @@ static void createPipeline( void )
.max_visible_surface_lights = MAX_VISIBLE_SURFACE_LIGHTS,
.light_grid_cell_size = LIGHT_GRID_CELL_SIZE,
.max_light_clusters = MAX_LIGHT_CLUSTERS,
.max_textures = MAX_TEXTURES,
.sbt_record_size = g_rtx.sbt_record_size,
};
const VkSpecializationMapEntry spec_map[] = {
{.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 = 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 = 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 = {
@ -381,6 +391,7 @@ static void createPipeline( void )
ShaderStageIndex_Miss_Shadow,
ShaderStageIndex_Miss_Empty,
ShaderStageIndex_ClosestHit,
ShaderStageIndex_ClosestHit_Shadow,
ShaderStageIndex_AnyHit_AlphaTest,
ShaderStageIndex_AnyHit_Additive,
ShaderStageIndex_COUNT,
@ -414,6 +425,7 @@ static void createPipeline( void )
DEFINE_SHADER("shadow.rmiss.spv", MISS, ShaderStageIndex_Miss_Shadow);
DEFINE_SHADER("empty.rmiss.spv", MISS, ShaderStageIndex_Miss_Empty);
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("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_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_Shadow, SHADER_OFFSET_HIT_SHADOW);
shader_groups[ShaderBindingTable_RayGen] = (VkRayTracingShaderGroupCreateInfoKHR) {
.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR,
@ -491,6 +504,15 @@ static void createPipeline( void )
.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));
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_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
for (int i = 0; i < MAX_TEXTURES; ++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].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);
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_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 };
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);
}
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,
};
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)
// samplers[i] = vk_core.default_sampler;
@ -1248,30 +1279,33 @@ qboolean VK_RayInit( void )
createPipeline();
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,
VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT );
SET_DEBUG_NAMEF(g_rtx.frames[i].denoised.image, VK_OBJECT_TYPE_IMAGE, "rtx frames[%d] denoised", i);
g_rtx.frames[i].base_color = VK_ImageCreate(FRAME_WIDTH, FRAME_HEIGHT, VK_FORMAT_R8G8B8A8_UNORM,
VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_STORAGE_BIT);
SET_DEBUG_NAMEF(g_rtx.frames[i].base_color.image, VK_OBJECT_TYPE_IMAGE, "rtx frames[%d] base_color", i);
g_rtx.frames[i].diffuse_gi = VK_ImageCreate(FRAME_WIDTH, FRAME_HEIGHT, VK_FORMAT_R16G16B16A16_SFLOAT,
VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_STORAGE_BIT);
SET_DEBUG_NAMEF(g_rtx.frames[i].diffuse_gi.image, VK_OBJECT_TYPE_IMAGE, "rtx frames[%d] diffuse_gi", i);
g_rtx.frames[i].specular = VK_ImageCreate(FRAME_WIDTH, FRAME_HEIGHT, VK_FORMAT_R16G16B16A16_SFLOAT,
VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_STORAGE_BIT);
SET_DEBUG_NAMEF(g_rtx.frames[i].specular.image, VK_OBJECT_TYPE_IMAGE, "rtx frames[%d] specular", i);
g_rtx.frames[i].additive = VK_ImageCreate(FRAME_WIDTH, FRAME_HEIGHT, VK_FORMAT_R16G16B16A16_SFLOAT,
VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_STORAGE_BIT);
SET_DEBUG_NAMEF(g_rtx.frames[i].additive.image, VK_OBJECT_TYPE_IMAGE, "rtx frames[%d] additive", i);
#define CREATE_GBUFFER_IMAGE(name, format_, add_usage_bits) \
do { \
char debug_name[64]; \
const xvk_image_create_t create = { \
.debug_name = debug_name, \
.width = FRAME_WIDTH, \
.height = FRAME_HEIGHT, \
.mips = 1, \
.layers = 1, \
.format = format_, \
.tiling = VK_IMAGE_TILING_OPTIMAL, \
.usage = VK_IMAGE_USAGE_STORAGE_BIT | add_usage_bits, \
.has_alpha = true, \
.is_cubemap = false, \
}; \
Q_snprintf(debug_name, sizeof(debug_name), "rtx frames[%d] " # name, i); \
g_rtx.frames[i].name = XVK_ImageCreate(&create); \
} while(0)
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
g_rtx.frames[i].normals = VK_ImageCreate(FRAME_WIDTH, FRAME_HEIGHT, VK_FORMAT_R16G16B16A16_SNORM,
VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_STORAGE_BIT);
SET_DEBUG_NAMEF(g_rtx.frames[i].normals.image, VK_OBJECT_TYPE_IMAGE, "rtx frames[%d] normals", i);
CREATE_GBUFFER_IMAGE(normals, VK_FORMAT_R16G16B16A16_SNORM, 0);
#undef CREATE_GBUFFER_IMAGE
}
if (vk_core.debug) {
@ -1287,12 +1321,12 @@ void VK_RayShutdown( void ) {
ASSERT(vk_core.rtx);
for (int i = 0; i < ARRAYSIZE(g_rtx.frames); ++i) {
VK_ImageDestroy(&g_rtx.frames[i].denoised);
VK_ImageDestroy(&g_rtx.frames[i].base_color);
VK_ImageDestroy(&g_rtx.frames[i].diffuse_gi);
VK_ImageDestroy(&g_rtx.frames[i].specular);
VK_ImageDestroy(&g_rtx.frames[i].additive);
VK_ImageDestroy(&g_rtx.frames[i].normals);
XVK_ImageDestroy(&g_rtx.frames[i].denoised);
XVK_ImageDestroy(&g_rtx.frames[i].base_color);
XVK_ImageDestroy(&g_rtx.frames[i].diffuse_gi);
XVK_ImageDestroy(&g_rtx.frames[i].specular);
XVK_ImageDestroy(&g_rtx.frames[i].additive);
XVK_ImageDestroy(&g_rtx.frames[i].normals);
}
vkDestroyPipeline(vk_core.device, g_rtx.pipeline, NULL);

View File

@ -48,11 +48,18 @@ void initTextures( void )
*/
}
static void unloadSkybox( void );
void destroyTextures( void )
{
for( unsigned int i = 0; i < vk_numTextures; 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( vk_texturesHashTable, 0, sizeof( vk_texturesHashTable ));
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 )
{
int dx2, dy, d;
@ -290,6 +299,22 @@ static void VK_CreateInternalTextures( void )
// cinematic dummy
pic = Common_FakeImage( 640, 100, 1, IMAGE_HAS_COLOR );
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)
@ -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)
{
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;
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);
int mipCount = 0;
// TODO non-rbga textures
// TODO cubemaps
if (!pic->buffer)
return false;
for (int i = 0; i < num_layers; ++i) {
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;
tex->height = pic->height;
mipCount = CalcMipmapCount( tex, ( buf != NULL ));
if (i == 0)
continue;
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
// // 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 )))
// data = GL_ResampleTexture( buf, pic->width, pic->height, tex->width, tex->height, normalMap );
// if(( tex->depth == 1 ) && (( layers->width != tex->width ) || ( layers->height != tex->height )))
// data = GL_ResampleTexture( buf, layers->width, layers->height, tex->width, tex->height, normalMap );
// 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 );
// 1. Create VkImage w/ usage = DST|SAMPLED, layout=UNDEFINED
{
VkImageCreateInfo image_create_info = {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.imageType = VK_IMAGE_TYPE_2D,
.extent.width = pic->width,
.extent.height = pic->height,
.extent.depth = 1,
const xvk_image_create_t create = {
.debug_name = tex->name,
.width = tex->width,
.height = tex->height,
.mips = mipCount,
.layers = num_layers,
.format = format,
.mipLevels = mipCount,
.arrayLayers = 1,
.tiling = tiling,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.usage = usage,
.samples = VK_SAMPLE_COUNT_1_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.tiling = VK_IMAGE_TILING_OPTIMAL,
.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
.has_alpha = layers[0]->flags & IMAGE_HAS_ALPHA,
.is_cubemap = cubemap,
};
XVK_CHECK(vkCreateImage(vk_core.device, &image_create_info, NULL, &tex->vk.image));
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));
tex->vk.image = XVK_ImageCreate(&create);
}
{
@ -517,7 +548,7 @@ static qboolean VK_UploadTexture(vk_texture_t *tex, rgbdata_t *pic)
// 5.1.1 transitionToLayout(UNDEFINED -> DST)
VkImageMemoryBarrier image_barrier = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.image = tex->vk.image,
.image = tex->vk.image.image,
.srcAccessMask = 0,
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
@ -527,7 +558,7 @@ static qboolean VK_UploadTexture(vk_texture_t *tex, rgbdata_t *pic)
.baseMipLevel = 0,
.levelCount = mipCount,
.baseArrayLayer = 0,
.layerCount = 1,
.layerCount = num_layers,
}};
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);
// 5.1.2 copyBufferToImage for all mip levels
{
for (int mip = 0; mip < mipCount; ++mip)
{
for (int layer = 0; layer < num_layers; ++layer) {
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 height = Q_max( 1, ( pic->height >> mip ));
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){
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = mip,
.baseArrayLayer = 0,
.baseArrayLayer = layer,
.layerCount = 1,
};
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
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;
}
@ -585,7 +617,7 @@ static qboolean VK_UploadTexture(vk_texture_t *tex, rgbdata_t *pic)
.baseMipLevel = 0,
.levelCount = mipCount,
.baseArrayLayer = 0,
.layerCount = 1,
.layerCount = num_layers,
};
vkCmdPipelineBarrier(vk_core.cb_tex,
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));
}
{
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:
// - per-texture desc sets can be inconvenient if texture is used in different incompatible contexts
// - update descriptor sets in batch?
if (vk_desc.next_free != MAX_TEXTURES)
{
VkDescriptorImageInfo dii_tex = {
.imageView = tex->vk.image_view,
.imageView = tex->vk.image.view,
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
};
VkWriteDescriptorSet wds[] = { {
@ -707,7 +724,7 @@ int VK_LoadTexture( const char *name, const byte *buf, size_t size, int flags )
// upload texture
VK_ProcessImage( tex, pic );
if( !VK_UploadTexture( tex, pic ))
if( !uploadTexture( tex, &pic, 1, false ))
{
memset( tex, 0, sizeof( vk_texture_t ));
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;
}
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 )
{
gEngine.Con_Printf("VK FIXME: %s\n", __FUNCTION__);
@ -756,7 +786,7 @@ void VK_FreeTexture( unsigned int texnum ) {
ASSERT( tex != NULL );
// already freed?
if( !tex->vk.image ) return;
if( !tex->vk.image.image ) return;
// debug
if( !tex->name[0] )
@ -787,9 +817,7 @@ void VK_FreeTexture( unsigned int texnum ) {
gEngine.FS_FreeImage( tex->original );
*/
vkDestroyImageView(vk_core.device, tex->vk.image_view, NULL);
vkDestroyImage(vk_core.device, tex->vk.image, NULL);
freeDeviceMemory(&tex->vk.device_memory);
XVK_ImageDestroy(&tex->vk.image);
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 );
if( !VK_UploadTexture( tex, pic ))
if( !uploadTexture( tex, &pic, 1, false ))
{
memset( tex, 0, sizeof( vk_texture_t ));
return 0;
@ -834,51 +862,6 @@ int VK_LoadTextureFromBuffer( const char *name, rgbdata_t *pic, texFlags_t flags
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 tex_id = 0;
char buffer[1024];
@ -892,25 +875,26 @@ int XVK_TextureLookupF( const char *fmt, ...) {
return tex_id;
}
static void unloadSkybox( void )
{
int i;
// release old skybox
for( i = 0; i < 6; i++ )
{
if( !tglob.skyboxTextures[i] ) continue;
VK_FreeTexture( tglob.skyboxTextures[i] );
static void unloadSkybox( void ) {
if (tglob.skybox_cube.vk.image.image) {
XVK_ImageDestroy(&tglob.skybox_cube.vk.image);
memset(&tglob.skybox_cube, 0, sizeof(tglob.skybox_cube));
}
tglob.skyboxbasenum = 5800; // set skybox base (to let some mods load hi-res skyboxes)
memset( tglob.skyboxTextures, 0, sizeof( tglob.skyboxTextures ));
tglob.fCustomSkybox = false;
}
static const char* r_skyBoxSuffix[6] = { "rt", "bk", "lf", "ft", "up", "dn" };
//static const int r_skyTexOrder[6] = { 0, 2, 1, 3, 4, 5 };
static struct {
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_HLSTYLE 1
@ -929,7 +913,7 @@ static int CheckSkybox( const char *name )
for( j = 0; j < 6; j++ )
{
// 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 ))
num_checked_sides++;
@ -941,7 +925,7 @@ static int CheckSkybox( const char *name )
for( j = 0; j < 6; j++ )
{
// 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 ))
num_checked_sides++;
}
@ -958,6 +942,8 @@ void XVK_SetupSky( const char *skyboxname )
char loadname[MAX_STRING];
char sidename[MAX_STRING];
int i, result, len;
rgbdata_t *sides[6];
qboolean success = false;
if( !COM_CheckString( skyboxname ))
{
@ -990,21 +976,43 @@ void XVK_SetupSky( const char *skyboxname )
for( i = 0; i < 6; i++ )
{
if( result == SKYBOX_HLSTYLE )
Q_snprintf( sidename, sizeof( sidename ), "%s%s", loadname, r_skyBoxSuffix[i] );
else 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, g_skybox_info[i].suffix );
tglob.skyboxTextures[i] = VK_LoadTexture( sidename, NULL, 0, TF_CLAMP|TF_SKY );
if( !tglob.skyboxTextures[i] ) break;
gEngine.Con_DPrintf( "%s%s%s", skyboxname, r_skyBoxSuffix[i], i != 5 ? ", " : ". " );
sides[i] = gEngine.FS_LoadImage( sidename, NULL, 0);
if (!sides[i] || !sides[i]->buffer)
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;
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
#include "vk_core.h"
#include "vk_image.h"
#include "xash3d_types.h"
#include "const.h"
@ -14,9 +15,7 @@ typedef struct vk_texture_s
uint texnum;
struct {
VkImage image;
VkImageView image_view;
device_memory_t device_memory;
xvk_image_t image;
VkDescriptorSet descriptor;
} vk;
@ -37,13 +36,15 @@ typedef struct vk_textures_global_s
int alphaskyTexture; // quake1 alpha-sky layer
int lightmapTextures[MAX_LIGHTMAPS];
int dlightTexture; // custom dlight texture
int skyboxTextures[6]; // skybox sides
int cinTexture; // cinematic texture
int skytexturenum; // this not a gl_texturenum!
int skyboxbasenum; // start with 5800 FIXME remove this, lewa says this is a GL1 hack
qboolean fCustomSkybox; // TODO do we need this for anything?
vk_texture_t skybox_cube;
vk_texture_t cubemap_placeholder;
} vk_textures_global_t;
// 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 );
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, ...);
#define VK_LoadTextureInternal( name, pic, flags ) VK_LoadTextureFromBuffer( name, pic, flags, false )
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);