yum-mirror/slang

Making it easier to work with shaders

git clone https://git.yummers.dev/yum-mirror/slang

Jay KwakEnable debug-layers for examples when not using test-mode (#8024)ef743e716

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