diff --git a/RT/RTmaterials.c b/RT/RTmaterials.c index d34de55..0ed6441 100644 --- a/RT/RTmaterials.c +++ b/RT/RTmaterials.c @@ -160,43 +160,79 @@ static int RT_LoadMaterialTexturesFromPaths(uint16_t bm_index, RT_Material *mate { if (load_mask & RT_BITN(i)) { + + // first try loading the compressed dds version of the file if it exists + bool ddsLoaded = false; RT_ArenaMemoryScope(&g_thread_arena) { - char *file = RT_ArenaPrintF(&g_thread_arena, "assets/textures/%s", paths->textures[i]); - - RT_TextureFormat format = g_rt_material_texture_slot_formats[i]; + char* ddsFile = RT_ArenaPrintF(&g_thread_arena, "assets/textures/%s.dds", paths->textures[i]); + + uint8_t* ddsData = NULL; // this is pointer to whole file including header + const struct DDS_HEADER* header = NULL; // pointer to the header (in same buffer as ddsData) + const uint8_t* bitData = NULL; // pointer to the compressed pixel data (in same buffer as ddsData) + size_t bitSize = 0; // size of the compressed pixel data + + bool fileLoaded = RT_LoadDDSImageFromDisk(&g_thread_arena, ddsFile, &ddsData, &header, &bitData, &bitSize); + + if (fileLoaded && ddsData) + { + ddsLoaded = true; + + material->textures[i] = RT_UploadTextureDDS(&(RT_UploadTextureParamsDDS) { + .header = header, + .ddsData = ddsData, + .bitData = bitData, + .bitSize = bitSize, + .sRGB = i == 0 || i == 4, // set sRGB to true if basecolor or emissive + .name = RT_ArenaPrintF(&g_thread_arena, "Game Texture %hu:%s (source: %s)", + bm_index, g_rt_texture_slot_names[i], ddsFile) + }); - int bpp = g_rt_texture_format_bpp[format]; + textures_reloaded += 1; + } - int w = 0, h = 0, c = 0; - unsigned char *pixels = RT_LoadImageFromDisk(&g_thread_arena, file, &w, &h, &c, bpp); + } - if (i == RT_MaterialTextureSlot_Emissive) + if (!ddsLoaded) // dds file not loaded try png + { + RT_ArenaMemoryScope(&g_thread_arena) { - // Premultiply emissive by alpha to avoid white transparent backgrounds from showing... + char* file = RT_ArenaPrintF(&g_thread_arena, "assets/textures/%s.png", paths->textures[i]); + + RT_TextureFormat format = g_rt_material_texture_slot_formats[i]; + + int bpp = g_rt_texture_format_bpp[format]; - uint32_t *at = (uint32_t *)pixels; - for (int i = 0; i < w*h; i++) + int w = 0, h = 0, c = 0; + unsigned char* pixels = RT_LoadImageFromDisk(&g_thread_arena, file, &w, &h, &c, bpp); + + if (i == RT_MaterialTextureSlot_Emissive) { - RT_Vec4 color = RT_UnpackRGBA(*at); - color.xyz = RT_Vec3Muls(color.xyz, color.w); - *at++ = RT_PackRGBA(color); + // Premultiply emissive by alpha to avoid white transparent backgrounds from showing... + + uint32_t* at = (uint32_t*)pixels; + for (size_t i = 0; i < w * h; i++) + { + RT_Vec4 color = RT_UnpackRGBA(*at); + color.xyz = RT_Vec3Muls(color.xyz, color.w); + *at++ = RT_PackRGBA(color); + } } - } - - if (pixels) - { - material->textures[i] = RT_UploadTexture(&(RT_UploadTextureParams){ - .format = format, - .width = w, - .height = h, - .pixels = pixels, - .pitch = bpp*w, - .name = RT_ArenaPrintF(&g_thread_arena, "Game Texture %hu:%s (source: %s)", - bm_index, g_rt_texture_slot_names[i], file), - }); - textures_reloaded += 1; + if (pixels) + { + material->textures[i] = RT_UploadTexture(&(RT_UploadTextureParams) { + .format = format, + .width = w, + .height = h, + .pixels = pixels, + .pitch = bpp * w, + .name = RT_ArenaPrintF(&g_thread_arena, "Game Texture %hu:%s (source: %s)", + bm_index, g_rt_texture_slot_names[i], file), + }); + + textures_reloaded += 1; + } } } } @@ -261,11 +297,11 @@ void RT_InitAllBitmaps(void) material->roughness = 0.5f; } - snprintf(paths->albedo_texture, sizeof(paths->albedo_texture), "%s_basecolor.png", bitmap_name); - snprintf(paths->normal_texture, sizeof(paths->normal_texture), "%s_normal.png", bitmap_name); - snprintf(paths->metalness_texture, sizeof(paths->metalness_texture), "%s_metallic.png", bitmap_name); - snprintf(paths->roughness_texture, sizeof(paths->roughness_texture), "%s_roughness.png", bitmap_name); - snprintf(paths->emissive_texture, sizeof(paths->emissive_texture), "%s_emissive.png", bitmap_name); + snprintf(paths->albedo_texture, sizeof(paths->albedo_texture), "%s_basecolor", bitmap_name); + snprintf(paths->normal_texture, sizeof(paths->normal_texture), "%s_normal", bitmap_name); + snprintf(paths->metalness_texture, sizeof(paths->metalness_texture), "%s_metallic", bitmap_name); + snprintf(paths->roughness_texture, sizeof(paths->roughness_texture), "%s_roughness", bitmap_name); + snprintf(paths->emissive_texture, sizeof(paths->emissive_texture), "%s_emissive", bitmap_name); // ------------------------------------------------------------------ @@ -274,6 +310,7 @@ void RT_InitAllBitmaps(void) bool default_metalness, default_roughness; RT_ParseMaterialDefinitionFile(bm_index, material, paths, &default_metalness, &default_roughness); + RT_LoadMaterialTexturesFromPaths(bm_index, material, paths, ~0u); if (default_metalness && RT_RESOURCE_HANDLE_VALID(material->metalness_texture)) diff --git a/RT/Renderer/Backend/Common/include/ImageReadWrite.h b/RT/Renderer/Backend/Common/include/ImageReadWrite.h index 6baf187..e04347b 100644 --- a/RT/Renderer/Backend/Common/include/ImageReadWrite.h +++ b/RT/Renderer/Backend/Common/include/ImageReadWrite.h @@ -1,6 +1,7 @@ #pragma once #include "ApiTypes.h" +#include "Renderer/Backend/DX12/DirectXTK12/src/DDSc.h" #pragma pack(push, 8) @@ -9,5 +10,6 @@ typedef struct RT_Arena RT_Arena; RT_API unsigned char *RT_LoadImageFromDisk(RT_Arena *arena, const char *path, int *w, int *h, int *channel_count, int required_channel_count); RT_API unsigned char *RT_LoadImageFromMemory(RT_Arena *arena, const void *memory, size_t memory_size, int *w, int *h, int *channel_count, int required_channel_count); RT_API void RT_WritePNGToDisk(const char *path, int w, int h, int channel_count, const void *pixels, int stride_in_bytes); +RT_API bool RT_LoadDDSImageFromDisk(RT_Arena* arena, const char* path, const uint8_t** ddsData, const struct DDS_HEADER** header,const uint8_t** bitData, size_t* bitSize); #pragma pack(pop) diff --git a/RT/Renderer/Backend/Common/include/Renderer.h b/RT/Renderer/Backend/Common/include/Renderer.h index 22eef77..e7b25a2 100644 --- a/RT/Renderer/Backend/Common/include/Renderer.h +++ b/RT/Renderer/Backend/Common/include/Renderer.h @@ -167,6 +167,16 @@ typedef struct RT_UploadTextureParams const char *name; // Optional, used for enhanced graphics debugging } RT_UploadTextureParams; +typedef struct RT_UploadTextureParamsDDS +{ + struct DDS_HEADER* header; + uint8_t* ddsData; + const uint8_t* bitData; + size_t bitSize; + bool sRGB; // force the format to a srgb format + const char* name; // Optional, used for enhanced graphics debugging +} RT_UploadTextureParamsDDS; + typedef struct RT_Triangle { // NOTE(daniel): I go through the effort of making these unions mostly to indicate @@ -312,6 +322,7 @@ RT_API uint16_t *RT_GetMaterialIndicesArray(void); RT_API void RT_DoRendererDebugMenus(const RT_DoRendererDebugMenuParams *params); RT_API RT_ResourceHandle RT_UploadTexture(const RT_UploadTextureParams* params); +RT_API RT_ResourceHandle RT_UploadTextureDDS(const RT_UploadTextureParamsDDS* params); // Updates the material on the GPU at the given index, so long as it is less than RT_MAX_MATERIALS. // Returns the material_index you passed in, or UINT16_MAX if it was out of bounds. RT_API uint16_t RT_UpdateMaterial(uint16_t material_index, const RT_Material *material); diff --git a/RT/Renderer/Backend/DX12/CMakeLists.txt b/RT/Renderer/Backend/DX12/CMakeLists.txt index 3d156a5..2b54540 100644 --- a/RT/Renderer/Backend/DX12/CMakeLists.txt +++ b/RT/Renderer/Backend/DX12/CMakeLists.txt @@ -38,6 +38,7 @@ set (Renderer_HEADER "CGLTF/cgltf.h" "src/DescriptorArena.hpp" "src/mikktspace.h" +"DirectXTK12/src/DDSc.h" "src/FSR2.h") #I hate cmake for not allowing me to link interface libs. Ah well, now we do this indirection! @@ -104,11 +105,13 @@ target_include_directories(Renderer PUBLIC include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/cimgui + ${CMAKE_CURRENT_SOURCE_DIR}/DirectXTK12/src ${CMAKE_CURRENT_SOURCE_DIR}/FSR2/include ) target_include_directories( Renderer PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/cimgui/imgui Renderer PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/implot + Renderer PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/DirectXTK12/src ) target_link_libraries(Renderer diff --git a/RT/Renderer/Backend/DX12/DirectXTK12/src/DDSc.h b/RT/Renderer/Backend/DX12/DirectXTK12/src/DDSc.h new file mode 100644 index 0000000..ef66598 --- /dev/null +++ b/RT/Renderer/Backend/DX12/DirectXTK12/src/DDSc.h @@ -0,0 +1,764 @@ +//-------------------------------------------------------------------------------------- +// DDS.h +// +// This header defines constants and structures that are useful when parsing +// DDS files. DDS files were originally designed to use several structures +// and constants that are native to DirectDraw and are defined in ddraw.h, +// such as DDSURFACEDESC2 and DDSCAPS2. This file defines similar +// (compatible) constants and structures so that one can use DDS files +// without needing to include ddraw.h. +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#ifndef __DDSC_H__ +#define __DDSC_H__ + +//#include +#include + +//namespace DirectX +//{ + +#pragma pack(push,1) + + + struct DDS_PIXELFORMAT + { + uint32_t size; + uint32_t flags; + uint32_t fourCC; + uint32_t RGBBitCount; + uint32_t RBitMask; + uint32_t GBitMask; + uint32_t BBitMask; + uint32_t ABitMask; + }; + +#define DDS_FOURCC 0x00000004 // DDPF_FOURCC +#define DDS_RGB 0x00000040 // DDPF_RGB +#define DDS_RGBA 0x00000041 // DDPF_RGB | DDPF_ALPHAPIXELS +#define DDS_LUMINANCE 0x00020000 // DDPF_LUMINANCE +#define DDS_LUMINANCEA 0x00020001 // DDPF_LUMINANCE | DDPF_ALPHAPIXELS +#define DDS_ALPHAPIXELS 0x00000001 // DDPF_ALPHAPIXELS +#define DDS_ALPHA 0x00000002 // DDPF_ALPHA +#define DDS_PAL8 0x00000020 // DDPF_PALETTEINDEXED8 +#define DDS_PAL8A 0x00000021 // DDPF_PALETTEINDEXED8 | DDPF_ALPHAPIXELS +#define DDS_BUMPDUDV 0x00080000 // DDPF_BUMPDUDV + // DDS_BUMPLUMINANCE 0x00040000 + +#ifndef MAKEFOURCC +#define MAKEFOURCC(ch0, ch1, ch2, ch3) \ + ((uint32_t)((uint8_t)(ch0)) \ + | ((uint32_t)((uint8_t)(ch1)) << 8) \ + | ((uint32_t)((uint8_t)(ch2)) << 16) \ + | ((uint32_t)((uint8_t)(ch3)) << 24)) +#endif /* MAKEFOURCC */ + +#ifndef DDSGLOBALCONST +#if defined(__GNUC__) && !defined(__MINGW32__) +#define DDSGLOBALCONST extern const __attribute__((weak)) +#else +#define DDSGLOBALCONST extern const __declspec(selectany) +#endif +#endif /* DDSGLOBALCONST */ + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_DXT1 = + { sizeof(struct DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','T','1'), 0, 0, 0, 0, 0 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_DXT2 = + { sizeof(struct DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','T','2'), 0, 0, 0, 0, 0 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_DXT3 = + { sizeof(struct DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','T','3'), 0, 0, 0, 0, 0 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_DXT4 = + { sizeof(struct DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','T','4'), 0, 0, 0, 0, 0 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_DXT5 = + { sizeof(struct DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','T','5'), 0, 0, 0, 0, 0 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_BC4_UNORM = + { sizeof(struct DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('B','C','4','U'), 0, 0, 0, 0, 0 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_BC4_SNORM = + { sizeof(struct DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('B','C','4','S'), 0, 0, 0, 0, 0 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_BC5_UNORM = + { sizeof(struct DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('B','C','5','U'), 0, 0, 0, 0, 0 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_BC5_SNORM = + { sizeof(struct DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('B','C','5','S'), 0, 0, 0, 0, 0 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_R8G8_B8G8 = + { sizeof(struct DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('R','G','B','G'), 0, 0, 0, 0, 0 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_G8R8_G8B8 = + { sizeof(struct DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('G','R','G','B'), 0, 0, 0, 0, 0 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_YUY2 = + { sizeof(struct DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('Y','U','Y','2'), 0, 0, 0, 0, 0 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_UYVY = + { sizeof(struct DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('U','Y','V','Y'), 0, 0, 0, 0, 0 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_A8R8G8B8 = + { sizeof(struct DDS_PIXELFORMAT), DDS_RGBA, 0, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_X8R8G8B8 = + { sizeof(struct DDS_PIXELFORMAT), DDS_RGB, 0, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_A8B8G8R8 = + { sizeof(struct DDS_PIXELFORMAT), DDS_RGBA, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_X8B8G8R8 = + { sizeof(struct DDS_PIXELFORMAT), DDS_RGB, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_G16R16 = + { sizeof(struct DDS_PIXELFORMAT), DDS_RGB, 0, 32, 0x0000ffff, 0xffff0000, 0, 0 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_R5G6B5 = + { sizeof(struct DDS_PIXELFORMAT), DDS_RGB, 0, 16, 0xf800, 0x07e0, 0x001f, 0 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_A1R5G5B5 = + { sizeof(struct DDS_PIXELFORMAT), DDS_RGBA, 0, 16, 0x7c00, 0x03e0, 0x001f, 0x8000 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_X1R5G5B5 = + { sizeof(struct DDS_PIXELFORMAT), DDS_RGB, 0, 16, 0x7c00, 0x03e0, 0x001f, 0 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_A4R4G4B4 = + { sizeof(struct DDS_PIXELFORMAT), DDS_RGBA, 0, 16, 0x0f00, 0x00f0, 0x000f, 0xf000 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_X4R4G4B4 = + { sizeof(struct DDS_PIXELFORMAT), DDS_RGB, 0, 16, 0x0f00, 0x00f0, 0x000f, 0 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_R8G8B8 = + { sizeof(struct DDS_PIXELFORMAT), DDS_RGB, 0, 24, 0xff0000, 0x00ff00, 0x0000ff, 0 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_A8R3G3B2 = + { sizeof(struct DDS_PIXELFORMAT), DDS_RGBA, 0, 16, 0x00e0, 0x001c, 0x0003, 0xff00 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_R3G3B2 = + { sizeof(struct DDS_PIXELFORMAT), DDS_RGB, 0, 8, 0xe0, 0x1c, 0x03, 0 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_A4L4 = + { sizeof(struct DDS_PIXELFORMAT), DDS_LUMINANCEA, 0, 8, 0x0f, 0, 0, 0xf0 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_L8 = + { sizeof(struct DDS_PIXELFORMAT), DDS_LUMINANCE, 0, 8, 0xff, 0, 0, 0 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_L16 = + { sizeof(struct DDS_PIXELFORMAT), DDS_LUMINANCE, 0, 16, 0xffff, 0, 0, 0 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_A8L8 = + { sizeof(struct DDS_PIXELFORMAT), DDS_LUMINANCEA, 0, 16, 0x00ff, 0, 0, 0xff00 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_A8L8_ALT = + { sizeof(struct DDS_PIXELFORMAT), DDS_LUMINANCEA, 0, 8, 0x00ff, 0, 0, 0xff00 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_L8_NVTT1 = + { sizeof(struct DDS_PIXELFORMAT), DDS_RGB, 0, 8, 0xff, 0, 0, 0 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_L16_NVTT1 = + { sizeof(struct DDS_PIXELFORMAT), DDS_RGB, 0, 16, 0xffff, 0, 0, 0 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_A8L8_NVTT1 = + { sizeof(struct DDS_PIXELFORMAT), DDS_RGBA, 0, 16, 0x00ff, 0, 0, 0xff00 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_A8 = + { sizeof(struct DDS_PIXELFORMAT), DDS_ALPHA, 0, 8, 0, 0, 0, 0xff }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_V8U8 = + { sizeof(struct DDS_PIXELFORMAT), DDS_BUMPDUDV, 0, 16, 0x00ff, 0xff00, 0, 0 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_Q8W8V8U8 = + { sizeof(struct DDS_PIXELFORMAT), DDS_BUMPDUDV, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 }; + + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_V16U16 = + { sizeof(struct DDS_PIXELFORMAT), DDS_BUMPDUDV, 0, 32, 0x0000ffff, 0xffff0000, 0, 0 }; + + // D3DFMT_A2R10G10B10/D3DFMT_A2B10G10R10 should be written using DX10 extension to avoid D3DX 10:10:10:2 reversal issue + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_A2R10G10B10 = + { sizeof(struct DDS_PIXELFORMAT), DDS_RGBA, 0, 32, 0x000003ff, 0x000ffc00, 0x3ff00000, 0xc0000000 }; + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_A2B10G10R10 = + { sizeof(struct DDS_PIXELFORMAT), DDS_RGBA, 0, 32, 0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000 }; + + // We do not support the following legacy Direct3D 9 formats: + // DDSPF_A2W10V10U10 = { sizeof(DDS_PIXELFORMAT), DDS_BUMPDUDV, 0, 32, 0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000 }; + // DDSPF_L6V5U5 = { sizeof(DDS_PIXELFORMAT), DDS_BUMPLUMINANCE, 0, 16, 0x001f, 0x03e0, 0xfc00, 0 }; + // DDSPF_X8L8V8U8 = { sizeof(DDS_PIXELFORMAT), DDS_BUMPLUMINANCE, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0 }; + + // This indicates the DDS_HEADER_DXT10 extension is present (the format is in dxgiFormat) + DDSGLOBALCONST struct DDS_PIXELFORMAT DDSPF_DX10 = + { sizeof(struct DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','1','0'), 0, 0, 0, 0, 0 }; + +#define DDS_HEADER_FLAGS_TEXTURE 0x00001007 // DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT +#define DDS_HEADER_FLAGS_MIPMAP 0x00020000 // DDSD_MIPMAPCOUNT +#define DDS_HEADER_FLAGS_VOLUME 0x00800000 // DDSD_DEPTH +#define DDS_HEADER_FLAGS_PITCH 0x00000008 // DDSD_PITCH +#define DDS_HEADER_FLAGS_LINEARSIZE 0x00080000 // DDSD_LINEARSIZE + +#define DDS_HEIGHT 0x00000002 // DDSD_HEIGHT +#define DDS_WIDTH 0x00000004 // DDSD_WIDTH + +#define DDS_SURFACE_FLAGS_TEXTURE 0x00001000 // DDSCAPS_TEXTURE +#define DDS_SURFACE_FLAGS_MIPMAP 0x00400008 // DDSCAPS_COMPLEX | DDSCAPS_MIPMAP +#define DDS_SURFACE_FLAGS_CUBEMAP 0x00000008 // DDSCAPS_COMPLEX + +#define DDS_CUBEMAP_POSITIVEX 0x00000600 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEX +#define DDS_CUBEMAP_NEGATIVEX 0x00000a00 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEX +#define DDS_CUBEMAP_POSITIVEY 0x00001200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEY +#define DDS_CUBEMAP_NEGATIVEY 0x00002200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEY +#define DDS_CUBEMAP_POSITIVEZ 0x00004200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEZ +#define DDS_CUBEMAP_NEGATIVEZ 0x00008200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEZ + +#define DDS_CUBEMAP_ALLFACES ( DDS_CUBEMAP_POSITIVEX | DDS_CUBEMAP_NEGATIVEX |\ + DDS_CUBEMAP_POSITIVEY | DDS_CUBEMAP_NEGATIVEY |\ + DDS_CUBEMAP_POSITIVEZ | DDS_CUBEMAP_NEGATIVEZ ) + +#define DDS_CUBEMAP 0x00000200 // DDSCAPS2_CUBEMAP + +#define DDS_FLAGS_VOLUME 0x00200000 // DDSCAPS2_VOLUME + + // Subset here matches D3D10_RESOURCE_DIMENSION and D3D11_RESOURCE_DIMENSION + enum DDS_RESOURCE_DIMENSION //: uint32_t + { + DDS_DIMENSION_TEXTURE1D = 2, + DDS_DIMENSION_TEXTURE2D = 3, + DDS_DIMENSION_TEXTURE3D = 4, + }; + + // Subset here matches D3D10_RESOURCE_MISC_FLAG and D3D11_RESOURCE_MISC_FLAG + enum DDS_RESOURCE_MISC_FLAG //: uint32_t + { + DDS_RESOURCE_MISC_TEXTURECUBE = 0x4L, + }; + + enum DDS_MISC_FLAGS2 //: uint32_t + { + DDS_MISC_FLAGS2_ALPHA_MODE_MASK = 0x7L, + }; + +#ifndef DDS_ALPHA_MODE_DEFINED +#define DDS_ALPHA_MODE_DEFINED + enum DDS_ALPHA_MODE //: uint32_t + { + DDS_ALPHA_MODE_UNKNOWN = 0, + DDS_ALPHA_MODE_STRAIGHT = 1, + DDS_ALPHA_MODE_PREMULTIPLIED = 2, + DDS_ALPHA_MODE_OPAQUE = 3, + DDS_ALPHA_MODE_CUSTOM = 4, + }; +#endif + + struct DDS_HEADER + { + uint32_t size; + uint32_t flags; + uint32_t height; + uint32_t width; + uint32_t pitchOrLinearSize; + uint32_t depth; // only if DDS_HEADER_FLAGS_VOLUME is set in flags + uint32_t mipMapCount; + uint32_t reserved1[11]; + struct DDS_PIXELFORMAT ddspf; + uint32_t caps; + uint32_t caps2; + uint32_t caps3; + uint32_t caps4; + uint32_t reserved2; + }; + + struct DDS_HEADER_DXT10 + { + enum DXGI_FORMAT dxgiFormat; + //uint8_t dxgiFormat; + uint32_t resourceDimension; + uint32_t miscFlag; // see D3D11_RESOURCE_MISC_FLAG + uint32_t arraySize; + uint32_t miscFlags2; // see DDS_MISC_FLAGS2 + }; + + //-------------------------------------------------------------------------------------- + // Return the BPP for a particular format + //-------------------------------------------------------------------------------------- + inline size_t DDSBitsPerPixel(_In_ DXGI_FORMAT fmt) + { + switch (fmt) + { + case DXGI_FORMAT_R32G32B32A32_TYPELESS: + case DXGI_FORMAT_R32G32B32A32_FLOAT: + case DXGI_FORMAT_R32G32B32A32_UINT: + case DXGI_FORMAT_R32G32B32A32_SINT: + return 128; + + case DXGI_FORMAT_R32G32B32_TYPELESS: + case DXGI_FORMAT_R32G32B32_FLOAT: + case DXGI_FORMAT_R32G32B32_UINT: + case DXGI_FORMAT_R32G32B32_SINT: + return 96; + + case DXGI_FORMAT_R16G16B16A16_TYPELESS: + case DXGI_FORMAT_R16G16B16A16_FLOAT: + case DXGI_FORMAT_R16G16B16A16_UNORM: + case DXGI_FORMAT_R16G16B16A16_UINT: + case DXGI_FORMAT_R16G16B16A16_SNORM: + case DXGI_FORMAT_R16G16B16A16_SINT: + case DXGI_FORMAT_R32G32_TYPELESS: + case DXGI_FORMAT_R32G32_FLOAT: + case DXGI_FORMAT_R32G32_UINT: + case DXGI_FORMAT_R32G32_SINT: + case DXGI_FORMAT_R32G8X24_TYPELESS: + case DXGI_FORMAT_D32_FLOAT_S8X24_UINT: + case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: + case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT: + case DXGI_FORMAT_Y416: + case DXGI_FORMAT_Y210: + case DXGI_FORMAT_Y216: + return 64; + + case DXGI_FORMAT_R10G10B10A2_TYPELESS: + case DXGI_FORMAT_R10G10B10A2_UNORM: + case DXGI_FORMAT_R10G10B10A2_UINT: + case DXGI_FORMAT_R11G11B10_FLOAT: + case DXGI_FORMAT_R8G8B8A8_TYPELESS: + case DXGI_FORMAT_R8G8B8A8_UNORM: + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + case DXGI_FORMAT_R8G8B8A8_UINT: + case DXGI_FORMAT_R8G8B8A8_SNORM: + case DXGI_FORMAT_R8G8B8A8_SINT: + case DXGI_FORMAT_R16G16_TYPELESS: + case DXGI_FORMAT_R16G16_FLOAT: + case DXGI_FORMAT_R16G16_UNORM: + case DXGI_FORMAT_R16G16_UINT: + case DXGI_FORMAT_R16G16_SNORM: + case DXGI_FORMAT_R16G16_SINT: + case DXGI_FORMAT_R32_TYPELESS: + case DXGI_FORMAT_D32_FLOAT: + case DXGI_FORMAT_R32_FLOAT: + case DXGI_FORMAT_R32_UINT: + case DXGI_FORMAT_R32_SINT: + case DXGI_FORMAT_R24G8_TYPELESS: + case DXGI_FORMAT_D24_UNORM_S8_UINT: + case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: + case DXGI_FORMAT_X24_TYPELESS_G8_UINT: + case DXGI_FORMAT_R9G9B9E5_SHAREDEXP: + case DXGI_FORMAT_R8G8_B8G8_UNORM: + case DXGI_FORMAT_G8R8_G8B8_UNORM: + case DXGI_FORMAT_B8G8R8A8_UNORM: + case DXGI_FORMAT_B8G8R8X8_UNORM: + case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: + case DXGI_FORMAT_B8G8R8A8_TYPELESS: + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: + case DXGI_FORMAT_B8G8R8X8_TYPELESS: + case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: + case DXGI_FORMAT_AYUV: + case DXGI_FORMAT_Y410: + case DXGI_FORMAT_YUY2: +#if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) + case DXGI_FORMAT_R10G10B10_7E3_A2_FLOAT: + case DXGI_FORMAT_R10G10B10_6E4_A2_FLOAT: + case DXGI_FORMAT_R10G10B10_SNORM_A2_UNORM: +#endif + return 32; + + case DXGI_FORMAT_P010: + case DXGI_FORMAT_P016: +#if (_WIN32_WINNT >= _WIN32_WINNT_WIN10) + case DXGI_FORMAT_V408: +#endif +#if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) + case DXGI_FORMAT_D16_UNORM_S8_UINT: + case DXGI_FORMAT_R16_UNORM_X8_TYPELESS: + case DXGI_FORMAT_X16_TYPELESS_G8_UINT: +#endif + return 24; + + case DXGI_FORMAT_R8G8_TYPELESS: + case DXGI_FORMAT_R8G8_UNORM: + case DXGI_FORMAT_R8G8_UINT: + case DXGI_FORMAT_R8G8_SNORM: + case DXGI_FORMAT_R8G8_SINT: + case DXGI_FORMAT_R16_TYPELESS: + case DXGI_FORMAT_R16_FLOAT: + case DXGI_FORMAT_D16_UNORM: + case DXGI_FORMAT_R16_UNORM: + case DXGI_FORMAT_R16_UINT: + case DXGI_FORMAT_R16_SNORM: + case DXGI_FORMAT_R16_SINT: + case DXGI_FORMAT_B5G6R5_UNORM: + case DXGI_FORMAT_B5G5R5A1_UNORM: + case DXGI_FORMAT_A8P8: + case DXGI_FORMAT_B4G4R4A4_UNORM: +#if (_WIN32_WINNT >= _WIN32_WINNT_WIN10) + case DXGI_FORMAT_P208: + case DXGI_FORMAT_V208: +#endif + return 16; + + case DXGI_FORMAT_NV12: + case DXGI_FORMAT_420_OPAQUE: + case DXGI_FORMAT_NV11: + return 12; + + case DXGI_FORMAT_R8_TYPELESS: + case DXGI_FORMAT_R8_UNORM: + case DXGI_FORMAT_R8_UINT: + case DXGI_FORMAT_R8_SNORM: + case DXGI_FORMAT_R8_SINT: + case DXGI_FORMAT_A8_UNORM: + case DXGI_FORMAT_BC2_TYPELESS: + case DXGI_FORMAT_BC2_UNORM: + case DXGI_FORMAT_BC2_UNORM_SRGB: + case DXGI_FORMAT_BC3_TYPELESS: + case DXGI_FORMAT_BC3_UNORM: + case DXGI_FORMAT_BC3_UNORM_SRGB: + case DXGI_FORMAT_BC5_TYPELESS: + case DXGI_FORMAT_BC5_UNORM: + case DXGI_FORMAT_BC5_SNORM: + case DXGI_FORMAT_BC6H_TYPELESS: + case DXGI_FORMAT_BC6H_UF16: + case DXGI_FORMAT_BC6H_SF16: + case DXGI_FORMAT_BC7_TYPELESS: + case DXGI_FORMAT_BC7_UNORM: + case DXGI_FORMAT_BC7_UNORM_SRGB: + case DXGI_FORMAT_AI44: + case DXGI_FORMAT_IA44: + case DXGI_FORMAT_P8: +#if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) + case DXGI_FORMAT_R4G4_UNORM: +#endif + return 8; + + case DXGI_FORMAT_R1_UNORM: + return 1; + + case DXGI_FORMAT_BC1_TYPELESS: + case DXGI_FORMAT_BC1_UNORM: + case DXGI_FORMAT_BC1_UNORM_SRGB: + case DXGI_FORMAT_BC4_TYPELESS: + case DXGI_FORMAT_BC4_UNORM: + case DXGI_FORMAT_BC4_SNORM: + return 4; + + case DXGI_FORMAT_UNKNOWN: + case DXGI_FORMAT_FORCE_UINT: + default: + return 0; + } + } + +//#define ISBITMASK( r,g,b,a ) ( ddpf.RBitMask == r && ddpf.GBitMask == g && ddpf.BBitMask == b && ddpf.ABitMask == a ) + inline bool ISBITMASK(const struct DDS_PIXELFORMAT* ddpf, uint32_t r, uint32_t g, uint32_t b, uint32_t a) { return ddpf->RBitMask == r && ddpf->GBitMask == g && ddpf->BBitMask == b && ddpf->ABitMask == a; } + + inline DXGI_FORMAT GetDXGIFormat(const struct DDS_PIXELFORMAT* ddpf) + { + if (ddpf->flags & DDS_RGB) + { + // Note that sRGB formats are written using the "DX10" extended header + + switch (ddpf->RGBBitCount) + { + case 32: + if (ISBITMASK(ddpf, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000)) + { + return DXGI_FORMAT_R8G8B8A8_UNORM; + } + + if (ISBITMASK(ddpf, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000)) + { + return DXGI_FORMAT_B8G8R8A8_UNORM; + } + + if (ISBITMASK(ddpf, 0x00ff0000, 0x0000ff00, 0x000000ff, 0)) + { + return DXGI_FORMAT_B8G8R8X8_UNORM; + } + + // No DXGI format maps to ISBITMASK(0x000000ff,0x0000ff00,0x00ff0000,0) aka D3DFMT_X8B8G8R8 + + // Note that many common DDS reader/writers (including D3DX) swap the + // the RED/BLUE masks for 10:10:10:2 formats. We assume + // below that the 'backwards' header mask is being used since it is most + // likely written by D3DX. The more robust solution is to use the 'DX10' + // header extension and specify the DXGI_FORMAT_R10G10B10A2_UNORM format directly + + // For 'correct' writers, this should be 0x000003ff,0x000ffc00,0x3ff00000 for RGB data + if (ISBITMASK(ddpf, 0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000)) + { + return DXGI_FORMAT_R10G10B10A2_UNORM; + } + + // No DXGI format maps to ISBITMASK(0x000003ff,0x000ffc00,0x3ff00000,0xc0000000) aka D3DFMT_A2R10G10B10 + + if (ISBITMASK(ddpf, 0x0000ffff, 0xffff0000, 0, 0)) + { + return DXGI_FORMAT_R16G16_UNORM; + } + + if (ISBITMASK(ddpf, 0xffffffff, 0, 0, 0)) + { + // Only 32-bit color channel format in D3D9 was R32F + return DXGI_FORMAT_R32_FLOAT; // D3DX writes this out as a FourCC of 114 + } + break; + + case 24: + // No 24bpp DXGI formats aka D3DFMT_R8G8B8 + break; + + case 16: + if (ISBITMASK(ddpf, 0x7c00, 0x03e0, 0x001f, 0x8000)) + { + return DXGI_FORMAT_B5G5R5A1_UNORM; + } + if (ISBITMASK(ddpf, 0xf800, 0x07e0, 0x001f, 0)) + { + return DXGI_FORMAT_B5G6R5_UNORM; + } + + // No DXGI format maps to ISBITMASK(0x7c00,0x03e0,0x001f,0) aka D3DFMT_X1R5G5B5 + + if (ISBITMASK(ddpf, 0x0f00, 0x00f0, 0x000f, 0xf000)) + { + return DXGI_FORMAT_B4G4R4A4_UNORM; + } + + // NVTT versions 1.x wrote this as RGB instead of LUMINANCE + if (ISBITMASK(ddpf, 0x00ff, 0, 0, 0xff00)) + { + return DXGI_FORMAT_R8G8_UNORM; + } + if (ISBITMASK(ddpf, 0xffff, 0, 0, 0)) + { + return DXGI_FORMAT_R16_UNORM; + } + + // No DXGI format maps to ISBITMASK(0x0f00,0x00f0,0x000f,0) aka D3DFMT_X4R4G4B4 + + // No 3:3:2:8 or paletted DXGI formats aka D3DFMT_A8R3G3B2, D3DFMT_A8P8, etc. + break; + + case 8: + // NVTT versions 1.x wrote this as RGB instead of LUMINANCE + if (ISBITMASK(ddpf, 0xff, 0, 0, 0)) + { + return DXGI_FORMAT_R8_UNORM; + } + + // No 3:3:2 or paletted DXGI formats aka D3DFMT_R3G3B2, D3DFMT_P8 + break; + } + } + else if (ddpf->flags & DDS_LUMINANCE) + { + switch (ddpf->RGBBitCount) + { + case 16: + if (ISBITMASK(ddpf, 0xffff, 0, 0, 0)) + { + return DXGI_FORMAT_R16_UNORM; // D3DX10/11 writes this out as DX10 extension + } + if (ISBITMASK(ddpf, 0x00ff, 0, 0, 0xff00)) + { + return DXGI_FORMAT_R8G8_UNORM; // D3DX10/11 writes this out as DX10 extension + } + break; + + case 8: + if (ISBITMASK(ddpf, 0xff, 0, 0, 0)) + { + return DXGI_FORMAT_R8_UNORM; // D3DX10/11 writes this out as DX10 extension + } + + // No DXGI format maps to ISBITMASK(0x0f,0,0,0xf0) aka D3DFMT_A4L4 + + if (ISBITMASK(ddpf, 0x00ff, 0, 0, 0xff00)) + { + return DXGI_FORMAT_R8G8_UNORM; // Some DDS writers assume the bitcount should be 8 instead of 16 + } + break; + } + } + else if (ddpf->flags & DDS_ALPHA) + { + if (8 == ddpf->RGBBitCount) + { + return DXGI_FORMAT_A8_UNORM; + } + } + else if (ddpf->flags & DDS_BUMPDUDV) + { + switch (ddpf->RGBBitCount) + { + case 32: + if (ISBITMASK(ddpf, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000)) + { + return DXGI_FORMAT_R8G8B8A8_SNORM; // D3DX10/11 writes this out as DX10 extension + } + if (ISBITMASK(ddpf, 0x0000ffff, 0xffff0000, 0, 0)) + { + return DXGI_FORMAT_R16G16_SNORM; // D3DX10/11 writes this out as DX10 extension + } + + // No DXGI format maps to ISBITMASK(0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000) aka D3DFMT_A2W10V10U10 + break; + + case 16: + if (ISBITMASK(ddpf, 0x00ff, 0xff00, 0, 0)) + { + return DXGI_FORMAT_R8G8_SNORM; // D3DX10/11 writes this out as DX10 extension + } + break; + } + + // No DXGI format maps to DDPF_BUMPLUMINANCE aka D3DFMT_L6V5U5, D3DFMT_X8L8V8U8 + } + else if (ddpf->flags & DDS_FOURCC) + { + if (MAKEFOURCC('D', 'X', 'T', '1') == ddpf->fourCC) + { + return DXGI_FORMAT_BC1_UNORM; + } + if (MAKEFOURCC('D', 'X', 'T', '3') == ddpf->fourCC) + { + return DXGI_FORMAT_BC2_UNORM; + } + if (MAKEFOURCC('D', 'X', 'T', '5') == ddpf->fourCC) + { + return DXGI_FORMAT_BC3_UNORM; + } + + // While pre-multiplied alpha isn't directly supported by the DXGI formats, + // they are basically the same as these BC formats so they can be mapped + if (MAKEFOURCC('D', 'X', 'T', '2') == ddpf->fourCC) + { + return DXGI_FORMAT_BC2_UNORM; + } + if (MAKEFOURCC('D', 'X', 'T', '4') == ddpf->fourCC) + { + return DXGI_FORMAT_BC3_UNORM; + } + + if (MAKEFOURCC('A', 'T', 'I', '1') == ddpf->fourCC) + { + return DXGI_FORMAT_BC4_UNORM; + } + if (MAKEFOURCC('B', 'C', '4', 'U') == ddpf->fourCC) + { + return DXGI_FORMAT_BC4_UNORM; + } + if (MAKEFOURCC('B', 'C', '4', 'S') == ddpf->fourCC) + { + return DXGI_FORMAT_BC4_SNORM; + } + + if (MAKEFOURCC('A', 'T', 'I', '2') == ddpf->fourCC) + { + return DXGI_FORMAT_BC5_UNORM; + } + if (MAKEFOURCC('B', 'C', '5', 'U') == ddpf->fourCC) + { + return DXGI_FORMAT_BC5_UNORM; + } + if (MAKEFOURCC('B', 'C', '5', 'S') == ddpf->fourCC) + { + return DXGI_FORMAT_BC5_SNORM; + } + + // BC6H and BC7 are written using the "DX10" extended header + + if (MAKEFOURCC('R', 'G', 'B', 'G') == ddpf->fourCC) + { + return DXGI_FORMAT_R8G8_B8G8_UNORM; + } + if (MAKEFOURCC('G', 'R', 'G', 'B') == ddpf->fourCC) + { + return DXGI_FORMAT_G8R8_G8B8_UNORM; + } + + if (MAKEFOURCC('Y', 'U', 'Y', '2') == ddpf->fourCC) + { + return DXGI_FORMAT_YUY2; + } + + // Check for D3DFORMAT enums being set here + switch (ddpf->fourCC) + { + case 36: // D3DFMT_A16B16G16R16 + return DXGI_FORMAT_R16G16B16A16_UNORM; + + case 110: // D3DFMT_Q16W16V16U16 + return DXGI_FORMAT_R16G16B16A16_SNORM; + + case 111: // D3DFMT_R16F + return DXGI_FORMAT_R16_FLOAT; + + case 112: // D3DFMT_G16R16F + return DXGI_FORMAT_R16G16_FLOAT; + + case 113: // D3DFMT_A16B16G16R16F + return DXGI_FORMAT_R16G16B16A16_FLOAT; + + case 114: // D3DFMT_R32F + return DXGI_FORMAT_R32_FLOAT; + + case 115: // D3DFMT_G32R32F + return DXGI_FORMAT_R32G32_FLOAT; + + case 116: // D3DFMT_A32B32G32R32F + return DXGI_FORMAT_R32G32B32A32_FLOAT; + + // No DXGI format maps to D3DFMT_CxV8U8 + } + } + + return DXGI_FORMAT_UNKNOWN; + } + + inline DXGI_FORMAT MakeSRGB( DXGI_FORMAT format) + { + switch (format) + { + case DXGI_FORMAT_R8G8B8A8_UNORM: + return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; + + case DXGI_FORMAT_BC1_UNORM: + return DXGI_FORMAT_BC1_UNORM_SRGB; + + case DXGI_FORMAT_BC2_UNORM: + return DXGI_FORMAT_BC2_UNORM_SRGB; + + case DXGI_FORMAT_BC3_UNORM: + return DXGI_FORMAT_BC3_UNORM_SRGB; + + case DXGI_FORMAT_B8G8R8A8_UNORM: + return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; + + case DXGI_FORMAT_B8G8R8X8_UNORM: + return DXGI_FORMAT_B8G8R8X8_UNORM_SRGB; + + case DXGI_FORMAT_BC7_UNORM: + return DXGI_FORMAT_BC7_UNORM_SRGB; + + default: + return format; + } + } + +#pragma pack(pop) + + static_assert(sizeof(struct DDS_PIXELFORMAT) == 32, "DDS pixel format size mismatch"); + static_assert(sizeof(struct DDS_HEADER) == 124, "DDS Header size mismatch"); + static_assert(sizeof(struct DDS_HEADER_DXT10) == 20, "DDS DX10 Extended Header size mismatch"); + +//} // namespace + +#endif \ No newline at end of file diff --git a/RT/Renderer/Backend/DX12/assets/shaders/include/common.hlsl b/RT/Renderer/Backend/DX12/assets/shaders/include/common.hlsl index 8d4551b..b21d62a 100644 --- a/RT/Renderer/Backend/DX12/assets/shaders/include/common.hlsl +++ b/RT/Renderer/Backend/DX12/assets/shaders/include/common.hlsl @@ -777,7 +777,8 @@ bool IsHitTransparent(uint instance_idx, uint primitive_idx, float2 barycentrics float4 albedo2 = tex_albedo_material2.SampleLevel(g_sampler_point_wrap, uv_rotated, 0); // Check for supertransparency pixel - if (all(albedo2.xyz == 1.0) && albedo2.a == 0.0) + // The reason the xyz check is 0.95 and not 1.0 like original game is to handle compressed textures slightly shifting colors + if (all(albedo2.xyz >= 0.95) && albedo2.a == 0.0) { return true; } diff --git a/RT/Renderer/Backend/DX12/src/ImageReadWrite.cpp b/RT/Renderer/Backend/DX12/src/ImageReadWrite.cpp index 21da31a..ecaae52 100644 --- a/RT/Renderer/Backend/DX12/src/ImageReadWrite.cpp +++ b/RT/Renderer/Backend/DX12/src/ImageReadWrite.cpp @@ -64,4 +64,110 @@ void RT_WritePNGToDisk(const char *path, int w, int h, int channel_count, const int result = stbi_write_png(path, w, h, channel_count, pixels, stride_in_bytes); RT_ASSERT(result); // TODO: Cool error handling / return an error code at least } +} + +// Code modified from DirectXTK12 : LoaderHelpers::LoadTextureDataFromFile (https://github.com/microsoft/DirectXTK12/blob/main/Src/LoaderHelpers.h) +bool RT_LoadDDSImageFromDisk(RT_Arena* arena, const char* path, const uint8_t** ddsData, const struct DDS_HEADER** header, const uint8_t** bitData, size_t* bitSize) +{ + RT_ArenaMarker marker = RT_ArenaGetMarker(&g_thread_arena); + + const uint32_t DDS_MAGIC = 0x20534444; // "DDS " + + FILE* f; + + if (0 == fopen_s(&f, path, "rb")) + { + long fileSize = 0; + + fseek(f, 0, SEEK_END); // seek to end of file + fileSize = ftell(f); // get current file pointer + fseek(f, 0, SEEK_SET); // seek back to beginning of file + + // success opening file + //printf("Found and Opened DDS Texture: %s, File Size: %d\n", path, fileSize); + + // check for file too big > 4294967296 (32bit) and handle + // Is this check necessary as fileSize is only a long?.. what happens if the file is bigger? + if (fileSize > 4294967296) + { + printf("Error: file too large\n"); + return false; + } + + // check for file too small // Need at least enough data to fill the header and magic number to be a valid DDS + if (fileSize < (sizeof(uint32_t) + sizeof(DDS_HEADER))) + { + printf("Error: file too small\n"); + return false; + } + + // read the contents of the file + *ddsData = (uint8_t*) RT_ArenaAlloc(arena, fileSize, 16); // allocate the memory to read the file + fread((void*)*ddsData, fileSize, 1, f); // read the file + + // close the file + fclose(f); + + // DDS files always start with the same magic number ("DDS ") (yes there's a space there) + if (memcmp(*ddsData, &DDS_MAGIC, 4) != 0) + { + printf("File did not begin with \"DDS \"\n"); + return false; + } + + // get the header data + *header = (DDS_HEADER*)(*ddsData + 4); + + // print header info + //printf("Size: %d, Flags: %d, Height: %d, Width: %d, PitchOrLinearSize: %d, Depth: %d, MipMapCount: %d, PixelFormat->Size: %d, PixelFormat->Flags: %d, PixelFormat->fourCC: %d, PixelFormat->RGBBitCount: %d\n", + // (*header)->size, (*header)->flags, (*header)->height, (*header)->width, (*header)->pitchOrLinearSize, (*header)->depth, (*header)->mipMapCount, (*header)->ddspf.size, (*header)->ddspf.flags, (*header)->ddspf.fourCC, (*header)->ddspf.RGBBitCount); + + // Verify header to validate DDS file + if ((*header)->size != sizeof(DDS_HEADER) || + (*header)->ddspf.size != sizeof(DDS_PIXELFORMAT)) + { + printf("Header not valid\n"); + return false; + } + + // Check for DX10 extension + bool bDXT10Header = false; + if (((*header)->ddspf.flags & DDS_FOURCC) && + (MAKEFOURCC('D', 'X', '1', '0') == (*header)->ddspf.fourCC)) + { + //printf("File has DX10 extension\n"); + + // Must be long enough for both headers and magic value + if (fileSize < (sizeof(uint32_t) + sizeof(DDS_HEADER) + sizeof(DDS_HEADER_DXT10))) + { + printf("File too small for DX10 extension\n"); + return false; + } + + bDXT10Header = true; + } + + // setup the pointers in the process request + //*header = hdr; + auto offset = sizeof(uint32_t) + sizeof(DDS_HEADER) + + (bDXT10Header ? sizeof(DDS_HEADER_DXT10) : 0u); + *bitData = *ddsData + offset; + *bitSize = fileSize - offset; + } + else + { + f = 0; + } + + if (arena != &g_thread_arena) + { + // stbi_load will always use the thread arena, but if we wanted the result to be in a different arena + // we'll just copy it. This means that the destination arena isn't polluted by intermediate allocations + // that stbi_load did to load the image. + //result = (unsigned char*)RT_ArenaCopy(arena, result, (*w) * (*h) * required_channel_count, 16); + // and then we know we don't need the thread arena stuff anymore so free it + RT_ArenaResetToMarker(&g_thread_arena, marker); + } + + return true; } \ No newline at end of file diff --git a/RT/Renderer/Backend/DX12/src/RenderBackend.cpp b/RT/Renderer/Backend/DX12/src/RenderBackend.cpp index de48ecf..d3317c3 100644 --- a/RT/Renderer/Backend/DX12/src/RenderBackend.cpp +++ b/RT/Renderer/Backend/DX12/src/RenderBackend.cpp @@ -3279,6 +3279,299 @@ RT_ResourceHandle RenderBackend::UploadTexture(const RT_UploadTextureParams& tex return g_texture_slotmap.Insert(resource); } +// Code modified from DirectXTK12 : DDSTextureLoader::CreateTextureFromDDS (https://github.com/microsoft/DirectXTK12/blob/main/Src/DDSTextureLoader.cpp) +RT_ResourceHandle RenderBackend::UploadTextureDDS(const RT_UploadTextureParamsDDS& texture_params) +{ + RT::MemoryScope temp; + + if (!texture_params.bitData) + { + printf("RenderBackend::UploadTextureDDS -- bitData empty returning checkerboard\n"); + return g_d3d.pink_checkerboard_texture; + } + + TextureResource resource = {}; + char* texture_resource_name = RT_ArenaPrintF(temp, "Texture: %s", texture_params.name); + + // first process the dds data + + const UINT width = texture_params.header->width; + UINT height = texture_params.header->height; + UINT depth = texture_params.header->depth; + + D3D12_RESOURCE_DIMENSION resDim = D3D12_RESOURCE_DIMENSION_UNKNOWN; + UINT arraySize = 1; + DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN; + bool isCubeMap = false; + + size_t mipCount = texture_params.header->mipMapCount; + if (0 == mipCount) + { + mipCount = 1; + } + + if ((texture_params.header->ddspf.flags & DDS_FOURCC) && + (MAKEFOURCC('D', 'X', '1', '0') == texture_params.header->ddspf.fourCC)) + { + DDS_HEADER_DXT10* d3d10ext = (DDS_HEADER_DXT10*)(texture_params.header + 1); // move pointer to end of DDS_HEADER and cast to DDS_HEADER_DXT10 (1 = size of DDS_HEADER) + + arraySize = d3d10ext->arraySize; + if (arraySize == 0) + { + printf("RenderBackend::UploadTextureDDS -- arraySize 0 returning checkerboard\n"); + return g_d3d.pink_checkerboard_texture; + } + + switch (d3d10ext->dxgiFormat) + { + case DXGI_FORMAT_NV12: + case DXGI_FORMAT_P010: + case DXGI_FORMAT_P016: + case DXGI_FORMAT_420_OPAQUE: + if ((d3d10ext->resourceDimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D) + || (width % 2) != 0 || (height % 2) != 0) + { + printf("RenderBackend::UploadTextureDDS -- ERROR: Video texture does not meet width/height requirements.\n"); + return g_d3d.pink_checkerboard_texture; + } + break; + + case DXGI_FORMAT_YUY2: + case DXGI_FORMAT_Y210: + case DXGI_FORMAT_Y216: + case DXGI_FORMAT_P208: + if ((width % 2) != 0) + { + printf("RenderBackend::UploadTextureDDS -- ERROR: Video texture does not meet width requirements.\n"); + return g_d3d.pink_checkerboard_texture; + } + break; + + case DXGI_FORMAT_NV11: + if ((width % 4) != 0) + { + printf("RenderBackend::UploadTextureDDS -- ERROR: Video texture does not meet width requirements.\n"); + return g_d3d.pink_checkerboard_texture; + } + break; + + case DXGI_FORMAT_AI44: + case DXGI_FORMAT_IA44: + case DXGI_FORMAT_P8: + case DXGI_FORMAT_A8P8: + printf("RenderBackend::UploadTextureDDS -- ERROR: Legacy stream video texture formats are not supported by Direct3D.\n"); + return g_d3d.pink_checkerboard_texture; + case DXGI_FORMAT_V208: + if ((d3d10ext->resourceDimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D) + || (height % 2) != 0) + { + printf("RenderBackend::UploadTextureDDS -- ERROR: Video texture does not meet height requirements.\n"); + return g_d3d.pink_checkerboard_texture; + } + break; + + default: + if (DDSBitsPerPixel(d3d10ext->dxgiFormat) == 0) + { + printf("RenderBackend::UploadTextureDDS -- ERROR: Unknown DXGI format (%u)\n", (uint32_t)(d3d10ext->dxgiFormat)); + return g_d3d.pink_checkerboard_texture; + } + break; + } + + + format = d3d10ext->dxgiFormat; + + switch (d3d10ext->resourceDimension) + { + case D3D12_RESOURCE_DIMENSION_TEXTURE1D: + // D3DX writes 1D textures with a fixed Height of 1 + if ((texture_params.header->flags & DDS_HEIGHT) && height != 1) + { + printf("RenderBackend::UploadTextureDDS -- ERROR: TEXTURE1D height not 1\n"); + return g_d3d.pink_checkerboard_texture; + } + height = depth = 1; + break; + + case D3D12_RESOURCE_DIMENSION_TEXTURE2D: + if (d3d10ext->miscFlag & 0x4 ) // RESOURCE_MISC_TEXTURECUBE + { + arraySize *= 6; + isCubeMap = true; + } + depth = 1; + break; + + case D3D12_RESOURCE_DIMENSION_TEXTURE3D: + if (!(texture_params.header->flags & DDS_HEADER_FLAGS_VOLUME)) + { + printf("RenderBackend::UploadTextureDDS -- ERROR: TEXTURE3D not flagged as volume\n"); + return g_d3d.pink_checkerboard_texture; + } + + if (arraySize > 1) + { + printf("RenderBackend::UploadTextureDDS -- ERROR: Volume textures are not texture arrays\n"); + return g_d3d.pink_checkerboard_texture; + } + break; + + case D3D12_RESOURCE_DIMENSION_BUFFER: + + printf("RenderBackend::UploadTextureDDS -- ERROR: Resource dimension buffer type not supported for textures\n"); + return g_d3d.pink_checkerboard_texture; + + case D3D12_RESOURCE_DIMENSION_UNKNOWN: + default: + + printf("RenderBackend::UploadTextureDDS -- ERROR: Unknown resource dimension (%u)\n", (uint32_t)(d3d10ext->resourceDimension)); + return g_d3d.pink_checkerboard_texture; + } + + resDim = (D3D12_RESOURCE_DIMENSION)(d3d10ext->resourceDimension); + } + else + { + format = GetDXGIFormat(&texture_params.header->ddspf); + + if (format == DXGI_FORMAT_UNKNOWN) + { + printf("RenderBackend::UploadTextureDDS -- ERROR: Legacy DDS format not supported\n"); + return g_d3d.pink_checkerboard_texture; + } + + if (texture_params.header->flags & DDS_HEADER_FLAGS_VOLUME) + { + resDim = D3D12_RESOURCE_DIMENSION_TEXTURE3D; + } + else + { + if (texture_params.header->caps2 & DDS_CUBEMAP) + { + // We require all six faces to be defined + if ((texture_params.header->caps2 & DDS_CUBEMAP_ALLFACES) != DDS_CUBEMAP_ALLFACES) + { + printf("RenderBackend::UploadTextureDDS -- ERROR: DirectX 12 does not support partial cubemaps\n"); + return g_d3d.pink_checkerboard_texture; + } + + arraySize = 6; + isCubeMap = true; + } + + depth = 1; + resDim = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + + // Note there's no way for a legacy Direct3D 9 DDS to express a '1D' texture + } + + assert(DDSBitsPerPixel(format) != 0); + } + + + // Bound sizes (for security purposes we don't trust DDS file metadata larger than the Direct3D hardware requirements) + if (mipCount > D3D12_REQ_MIP_LEVELS) + { + printf("RenderBackend::UploadTextureDDS -- ERROR: Too many mipmap levels defined for DirectX 12\n"); + return g_d3d.pink_checkerboard_texture; + } + + switch (resDim) + { + case D3D12_RESOURCE_DIMENSION_TEXTURE1D: + if ((arraySize > D3D12_REQ_TEXTURE1D_ARRAY_AXIS_DIMENSION) || + (width > D3D12_REQ_TEXTURE1D_U_DIMENSION)) + { + printf("RenderBackend::UploadTextureDDS -- ERROR: Resource dimensions too large for DirectX 12 (1D: array %u, size %u)\n", arraySize, width); + return g_d3d.pink_checkerboard_texture; + } + break; + + case D3D12_RESOURCE_DIMENSION_TEXTURE2D: + if (isCubeMap) + { + // This is the right bound because we set arraySize to (NumCubes*6) above + if ((arraySize > D3D12_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION) || + (width > D3D12_REQ_TEXTURECUBE_DIMENSION) || + (height > D3D12_REQ_TEXTURECUBE_DIMENSION)) + { + + printf("RenderBackend::UploadTextureDDS -- ERROR: Resource dimensions too large for DirectX 12 (2D cubemap: array %u, size %u by %u)\n", arraySize, width, height); + return g_d3d.pink_checkerboard_texture; + } + } + else if ((arraySize > D3D12_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION) || + (width > D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION) || + (height > D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION)) + { + + printf("RenderBackend::UploadTextureDDS -- ERROR: Resource dimensions too large for DirectX 12 (2D: array %u, size %u by %u)\n", arraySize, width, height); + return g_d3d.pink_checkerboard_texture; + } + break; + + case D3D12_RESOURCE_DIMENSION_TEXTURE3D: + if ((arraySize > 1) || + (width > D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) || + (height > D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) || + (depth > D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION)) + { + + printf("RenderBackend::UploadTextureDDS -- ERROR: Resource dimensions too large for DirectX 12 (3D: array %u, size %u by %u by %u)\n", arraySize, width, height, depth); + return g_d3d.pink_checkerboard_texture; + } + break; + + case D3D12_RESOURCE_DIMENSION_BUFFER: + + printf("RenderBackend::UploadTextureDDS -- ERROR: Resource dimension buffer type not supported for textures\n"); + return g_d3d.pink_checkerboard_texture; + + default: + + printf("RenderBackend::UploadTextureDDS -- ERROR: Unknown resource dimension (%u)\n", (uint32_t)(resDim)); + return g_d3d.pink_checkerboard_texture; + } + + + /* Additional checks not implemented from DirectXTK12 + const UINT numberOfPlanes = D3D12GetFormatPlaneCount(d3dDevice, format); + if (!numberOfPlanes) + return E_INVALIDARG; + + if ((numberOfPlanes > 1) && IsDepthStencil(format)) + { + // DirectX 12 uses planes for stencil, DirectX 11 does not + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + if (outIsCubeMap != nullptr) + { + *outIsCubeMap = isCubeMap; + } + */ + + // convert to srgb if needed + if (texture_params.sRGB) + format = MakeSRGB(format); + + // decide how many mipmaps to create + double fullPowerOf2 = log(width) / log(2); // what power of two is the full texture size + double minPowerOf2 = log(64) / log(2); // power of two for 64px. (this is the minimum mip map size we want to use. Below 64px dds files start to get packed inefficiently into memory and the visual difference is not noticeable .) + size_t finalMipCount = lround(fullPowerOf2 - minPowerOf2) + 1; + + resource.texture = RT_CreateTexture(Utf16FromUtf8(temp, texture_resource_name), format, D3D12_RESOURCE_FLAG_NONE, (size_t)width, (uint32_t)height, + D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, (uint16_t)finalMipCount); + + UploadTextureDataDDS(resource.texture, texture_params.header->width, texture_params.header->height, DDSBitsPerPixel(format) , finalMipCount, texture_params.bitData); + + resource.descriptors = g_d3d.cbv_srv_uav.Allocate(1); + CreateTextureSRV(resource.texture, resource.descriptors.GetCPUDescriptor(0), format, (uint32_t)finalMipCount); + + return g_texture_slotmap.Insert(resource); +} + RT_ResourceHandle RenderBackend::UploadMesh(const RT_UploadMeshParams& mesh_params) { MemoryScope temp_arena; diff --git a/RT/Renderer/Backend/DX12/src/RenderBackend.h b/RT/Renderer/Backend/DX12/src/RenderBackend.h index 3906c18..d4f68a2 100644 --- a/RT/Renderer/Backend/DX12/src/RenderBackend.h +++ b/RT/Renderer/Backend/DX12/src/RenderBackend.h @@ -25,6 +25,7 @@ namespace RenderBackend void DoDebugMenus(const RT_DoRendererDebugMenuParams *params); RT_ResourceHandle UploadTexture(const RT_UploadTextureParams& texture_params); + RT_ResourceHandle UploadTextureDDS(const RT_UploadTextureParamsDDS& texture_params); RT_ResourceHandle UploadMesh(const RT_UploadMeshParams& mesh_params); void ReleaseTexture(const RT_ResourceHandle texture_handle); void ReleaseMesh(const RT_ResourceHandle mesh_handle); diff --git a/RT/Renderer/Backend/DX12/src/Renderer.cpp b/RT/Renderer/Backend/DX12/src/Renderer.cpp index b82bca3..72ff005 100644 --- a/RT/Renderer/Backend/DX12/src/Renderer.cpp +++ b/RT/Renderer/Backend/DX12/src/Renderer.cpp @@ -74,6 +74,11 @@ RT_ResourceHandle RT_UploadTexture(const RT_UploadTextureParams* params) return RenderBackend::UploadTexture(*params); } +RT_ResourceHandle RT_UploadTextureDDS(const RT_UploadTextureParamsDDS* params) +{ + return RenderBackend::UploadTextureDDS(*params); +} + RT_ResourceHandle RT_GetDefaultWhiteTexture(void) { return RenderBackend::GetDefaultWhiteTexture(); diff --git a/RT/Renderer/Backend/DX12/src/Resource.cpp b/RT/Renderer/Backend/DX12/src/Resource.cpp index ae9f5e0..5329340 100644 --- a/RT/Renderer/Backend/DX12/src/Resource.cpp +++ b/RT/Renderer/Backend/DX12/src/Resource.cpp @@ -265,6 +265,68 @@ namespace RT CopyTextureRegion(command_list, dst, 0, 0, 0, &src_loc, nullptr); } + void UploadTextureDataDDS(ID3D12Resource* dst, size_t width, size_t height, size_t bpp, size_t mipCount, const void* data_ptr) + { + uint8_t* src_ptr = (uint8_t*)data_ptr; // keep track of where we are in the texture data between mipmaps + + for (size_t mipIndex = 0; mipIndex < mipCount ; ++mipIndex) + { + + size_t divider = (size_t)round(pow(2, (double) mipIndex)); + + D3D12_PLACED_SUBRESOURCE_FOOTPRINT pitch_footprint; + D3D12_RESOURCE_DESC dst_desc = dst->GetDesc(); + size_t dst_byte_size; // total size of destination memory + size_t dst_row_byte_size; // row size of destination memory + UINT dst_row_num; // how many rows of data are there. this is different than height as dds is in 4x4 blocks of data + g_d3d.device->GetCopyableFootprints(&dst_desc,(UINT) mipIndex, 1, 0, &pitch_footprint, &dst_row_num, &dst_row_byte_size, &dst_byte_size); + + size_t src_byte_size = ((width / divider) * (height / divider)) / (bpp / 8); // total size of source memory + size_t src_row_byte_size = src_byte_size / dst_row_num; // row size of source memory + + RingBufferAllocation ring_buf_alloc = AllocateFromRingBuffer(&g_d3d.resource_upload_ring_buffer, dst_byte_size, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT); + D3D12_PLACED_SUBRESOURCE_FOOTPRINT placed_footprint = {}; + placed_footprint.Offset = ring_buf_alloc.byte_offset; + placed_footprint.Footprint = pitch_footprint.Footprint; + + uint8_t* dst_ptr = ring_buf_alloc.ptr; + + size_t dst_pitch = pitch_footprint.Footprint.RowPitch; + + // copy the texture data into the ring buffer + for (size_t y = 0; y < dst_row_num; ++y) + { + memcpy(dst_ptr, src_ptr, src_row_byte_size); + + // Increment the src and dst pointers. + // They are incremented by different amounts because the destination memory each row must be a multiple of 256bytes so padding is added to every row that is below this multiple. + // The dds file does not have this requirement and is more tightly packed. + src_ptr += src_row_byte_size; + dst_ptr += dst_pitch; + } + + + D3D12_TEXTURE_COPY_LOCATION src_loc = {}; + src_loc.pResource = ring_buf_alloc.resource; + src_loc.PlacedFootprint = placed_footprint; + src_loc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; + + D3D12_TEXTURE_COPY_LOCATION dest_loc = {}; + dest_loc.pResource = dst; + dest_loc.SubresourceIndex = (UINT)mipIndex; + dest_loc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + + D3D12_RESOURCE_STATES dst_state = g_d3d.resource_tracker.GetResourceState(dst); + + CommandList& command_list = *ring_buf_alloc.command_list; + ResourceTransition(command_list, dst, D3D12_RESOURCE_STATE_COPY_DEST); + command_list->CopyTextureRegion(&dest_loc, 0, 0, 0, &src_loc, nullptr); + ResourceTransition(command_list, dst, dst_state); + + } + + } + void CreateTextureSRV(ID3D12Resource* resource, D3D12_CPU_DESCRIPTOR_HANDLE descriptor, DXGI_FORMAT format, uint32_t mips) { D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc = {}; diff --git a/RT/Renderer/Backend/DX12/src/Resource.h b/RT/Renderer/Backend/DX12/src/Resource.h index 3069f87..09755c9 100644 --- a/RT/Renderer/Backend/DX12/src/Resource.h +++ b/RT/Renderer/Backend/DX12/src/Resource.h @@ -47,6 +47,7 @@ namespace RT void CopyTextureRegion(ID3D12GraphicsCommandList* command_list, ID3D12Resource* dst, uint32_t dst_x, uint32_t dst_y, uint32_t dst_z, const D3D12_TEXTURE_COPY_LOCATION* src_loc, const D3D12_BOX* src_box); void UploadTextureData(ID3D12Resource* dst, size_t row_pitch, size_t row_count, const void* data_ptr); + void UploadTextureDataDDS(ID3D12Resource* dst, size_t width, size_t height, size_t bpp, size_t mipCount, const void* data_ptr); void CreateTextureSRV(ID3D12Resource* resource, D3D12_CPU_DESCRIPTOR_HANDLE descriptor, DXGI_FORMAT format, uint32_t mips = UINT32_MAX); void CreateTextureUAV(ID3D12Resource* resource, D3D12_CPU_DESCRIPTOR_HANDLE descriptor, DXGI_FORMAT format);