yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
ef743e716
master
1#include "example-base.h" 2 3#include "slang.h" 4 5#include <chrono> 6 7#ifdef _WIN32 8#include <windows.h> 9#endif 10 11#define STB_IMAGE_IMPLEMENTATION 12#include "stb_image.h" 13 14using namespace Slang ; 15using namespace rhi ; 16 17class DebugCallback :public rhi::IDebugCallback 18{ 19public : 20virtual SLANG_NO_THROW void SLANG_MCALL handleMessage ( 21 rhi::DebugMessageType type , 22 rhi::DebugMessageSource source , 23const char * message )override 24 { 25const char * typeStr = "" ; 26switch (type ) 27 { 28case rhi::DebugMessageType ::Info : 29typeStr = "INFO: " ; 30break ; 31case rhi::DebugMessageType ::Warning : 32typeStr = "WARNING: " ; 33break ; 34case rhi::DebugMessageType ::Error : 35typeStr = "ERROR: " ; 36break ; 37default : 38break ; 39 } 40const char * sourceStr = "[GraphicsLayer]: " ; 41switch (source ) 42 { 43case rhi::DebugMessageSource ::Slang : 44sourceStr = "[Slang]: " ; 45break ; 46case rhi::DebugMessageSource ::Driver : 47sourceStr = "[Driver]: " ; 48break ; 49 } 50printf ("%s%s%s\n" ,sourceStr ,typeStr ,message ); 51#ifdef _WIN32 52OutputDebugStringA (sourceStr ); 53OutputDebugStringA (typeStr ); 54OutputDebugStringW (String (message ).toWString ()); 55OutputDebugStringW (L"\n" ); 56#endif 57 } 58}; 59 60 61Slang ::Result WindowedAppBase ::initializeBase ( 62const char * title , 63int width , 64int height , 65DeviceType deviceType ) 66{ 67DeviceDesc deviceDesc = {}; 68deviceDesc .deviceType = deviceType ; 69 70// Enable validation when not in test mode to avoid output pollution during testing 71deviceDesc .enableValidation = !isTestMode (); 72 73// Set debug callback (only used when validation is enabled, i.e., non-test mode) 74static DebugCallback debugCallback ; 75deviceDesc .debugCallback = & debugCallback ; 76 77 slang::CompilerOptionEntry slangOptions []= { 78 {slang::CompilerOptionName ::EmitSpirvDirectly , {slang::CompilerOptionValueKind ::Int ,1 }}, 79 {slang::CompilerOptionName ::DebugInformation , 80 {slang::CompilerOptionValueKind ::Int ,SLANG_DEBUG_INFO_LEVEL_STANDARD }}}; 81deviceDesc .slang .compilerOptionEntries = slangOptions ; 82 83// When in test mode, don't include debug information to avoid altering hash values during 84// testing Otherwise, include debug information for better debugging experience 85deviceDesc .slang .compilerOptionEntryCount = isTestMode () ?1 :2 ; 86 87gDevice = getRHI ()-> createDevice (deviceDesc ); 88if (!gDevice ) 89 { 90return SLANG_FAIL ; 91 } 92 93gQueue = gDevice -> getQueue (QueueType ::Graphics ); 94windowWidth = width ; 95windowHeight = height ; 96 97// Do not create swapchain and windows in test mode, because there won't be any display. 98if (!isTestMode ()) 99 { 100// Create a window for our application to render into. 101// 102 platform::WindowDesc windowDesc ; 103windowDesc .title = title ; 104windowDesc .width = width ; 105windowDesc .height = height ; 106windowDesc .style = platform::WindowStyle ::Default ; 107gWindow = platform::Application ::createWindow (windowDesc ); 108gWindow -> events .mainLoop = [this ]() {mainLoop (); }; 109gWindow -> events .sizeChanged = Slang ::Action <> (this ,& WindowedAppBase ::windowSizeChanged ); 110 111 112WindowHandle windowHandle = gWindow -> getNativeHandle ().convert < WindowHandle > (); 113gSurface = gDevice -> createSurface (windowHandle ); 114 115auto deviceInfo = gDevice -> getInfo (); 116Slang ::StringBuilder titleSb ; 117titleSb <<title <<" (" <<deviceInfo .apiName <<": " <<deviceInfo .adapterName <<")" ; 118gWindow -> setText (titleSb .getBuffer ()); 119 120 rhi::SurfaceConfig surfaceConfig = {}; 121 122surfaceConfig .format = gSurface -> getInfo ().preferredFormat ; 123surfaceConfig .width = width ; 124surfaceConfig .height = height ; 125surfaceConfig .desiredImageCount = kSwapchainImageCount ; 126gSurface -> configure (surfaceConfig ); 127 } 128else 129 { 130createOfflineTextures (); 131 } 132 133return SLANG_OK ; 134} 135 136void WindowedAppBase ::mainLoop () 137{ 138auto texture = gSurface -> acquireNextImage (); 139renderFrame (texture ); 140} 141 142 143ComPtr < ITextureView > WindowedAppBase ::createTextureFromFile ( 144String fileName , 145int & textureWidth , 146int & textureHeight ) 147{ 148int channelsInFile = 0 ; 149auto textureContent = 150stbi_load (fileName .getBuffer (),& textureWidth ,& textureHeight ,& channelsInFile ,4 ); 151TextureDesc textureDesc = {}; 152textureDesc .type = TextureType ::Texture2D ; 153textureDesc .usage = TextureUsage ::ShaderResource ; 154textureDesc .format = Format ::RGBA8Unorm ; 155textureDesc .mipCount = Math ::Log2Ceil (Math ::Min (textureWidth ,textureHeight ))+ 1 ; 156textureDesc .size .width = textureWidth ; 157textureDesc .size .height = textureHeight ; 158textureDesc .size .depth = 1 ; 159List < SubresourceData > subresData ; 160List < List < uint32_t >> mipMapData ; 161mipMapData .setCount (textureDesc .mipCount ); 162subresData .setCount (textureDesc .mipCount ); 163mipMapData [0 ].setCount (textureWidth * textureHeight ); 164memcpy (mipMapData [0 ].getBuffer (),textureContent ,textureWidth * textureHeight * 4 ); 165stbi_image_free (textureContent ); 166subresData [0 ].data = mipMapData [0 ].getBuffer (); 167subresData [0 ].rowPitch = textureWidth * 4 ; 168subresData [0 ].slicePitch = textureWidth * textureHeight * 4 ; 169 170// Build mipmaps. 171struct RGBA 172 { 173uint8_t v [4 ]; 174 }; 175auto castToRGBA = [](uint32_t v ) 176 { 177RGBA result ; 178memcpy (& result ,& v ,4 ); 179return result ; 180 }; 181auto castToUint = [](RGBA v ) 182 { 183uint32_t result ; 184memcpy (& result ,& v ,4 ); 185return result ; 186 }; 187 188int lastMipWidth = textureWidth ; 189int lastMipHeight = textureHeight ; 190for (uint32_t m = 1 ;m < textureDesc .mipCount ;m ++ ) 191 { 192auto lastMipmapData = mipMapData [m - 1 ].getBuffer (); 193int w = lastMipWidth /2 ; 194int h = lastMipHeight /2 ; 195mipMapData [m ].setCount (w * h ); 196subresData [m ].data = mipMapData [m ].getBuffer (); 197subresData [m ].rowPitch = w * 4 ; 198subresData [m ].slicePitch = h * w * 4 ; 199for (int x = 0 ;x < w ;x ++ ) 200 { 201for (int y = 0 ;y < h ;y ++ ) 202 { 203auto pix1 = castToRGBA (lastMipmapData [(y * 2 )* lastMipWidth + (x * 2 )]); 204auto pix2 = castToRGBA (lastMipmapData [(y * 2 )* lastMipWidth + (x * 2 + 1 )]); 205auto pix3 = castToRGBA (lastMipmapData [(y * 2 + 1 )* lastMipWidth + (x * 2 )]); 206auto pix4 = castToRGBA (lastMipmapData [(y * 2 + 1 )* lastMipWidth + (x * 2 + 1 )]); 207RGBA pix ; 208for (int c = 0 ;c < 4 ;c ++ ) 209 { 210pix .v [c ]= 211 (uint8_t )(((uint32_t )pix1 .v [c ]+ pix2 .v [c ]+ pix3 .v [c ]+ pix4 .v [c ]) /4 ); 212 } 213mipMapData [m ][y * w + x ]= castToUint (pix ); 214 } 215 } 216lastMipWidth = w ; 217lastMipHeight = h ; 218 } 219 220auto texture = gDevice -> createTexture (textureDesc ,subresData .getBuffer ()); 221 222TextureViewDesc viewDesc = {}; 223return gDevice -> createTextureView (texture .get (),viewDesc ); 224} 225 226void WindowedAppBase ::createOfflineTextures () 227{ 228for (uint32_t i = 0 ;i < kSwapchainImageCount ;i ++ ) 229 { 230TextureDesc textureDesc = {}; 231textureDesc .size .width = this -> windowWidth ; 232textureDesc .size .height = this -> windowHeight ; 233textureDesc .format = Format ::RGBA8Unorm ; 234textureDesc .mipCount = 1 ; 235textureDesc .usage = TextureUsage ::UnorderedAccess |TextureUsage ::CopySource ; 236auto texture = gDevice -> createTexture (textureDesc ); 237gOfflineTextures .add (texture ); 238 } 239} 240 241void WindowedAppBase ::offlineRender () 242{ 243SLANG_ASSERT (gOfflineTextures .getCount ()> 0 ); 244renderFrame (gOfflineTextures [0 ]); 245} 246 247void WindowedAppBase ::windowSizeChanged () 248{ 249// Wait for the GPU to finish. 250gQueue -> waitOnHost (); 251 252auto clientRect = gWindow -> getClientRect (); 253if (clientRect .width > 0 && clientRect .height > 0 ) 254 { 255SurfaceConfig config = {}; 256config .format = gSurface -> getInfo ().preferredFormat ; 257config .width = clientRect .width ; 258config .height = clientRect .height ; 259config .vsync = false; 260gSurface -> configure (config ); 261 } 262} 263 264int64_t getCurrentTime () 265{ 266return std::chrono::high_resolution_clock::now ().time_since_epoch ().count (); 267} 268 269int64_t getTimerFrequency () 270{ 271return std::chrono::high_resolution_clock::period::den ; 272} 273 274 275#ifdef _WIN32 276void _Win32OutputDebugString (const char * str ) 277{ 278OutputDebugStringW (Slang ::String (str ).toWString ().begin ()); 279} 280#endif