|  | /* | 
|  | * Copyright (C) 2017 Apple Inc. All rights reserved. | 
|  | * | 
|  | * Redistribution and use in source and binary forms, with or without | 
|  | * modification, are permitted provided that the following conditions | 
|  | * are met: | 
|  | * 1. Redistributions of source code must retain the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer. | 
|  | * 2. Redistributions in binary form must reproduce the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer in the | 
|  | *    documentation and/or other materials provided with the distribution. | 
|  | * | 
|  | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | 
|  | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
|  | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | 
|  | * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR | 
|  | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | 
|  | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | 
|  | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | 
|  | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | 
|  | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
|  | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
|  | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  | */ | 
|  |  | 
|  | // Example.cpp : Defines the entry point for the application. | 
|  | // | 
|  |  | 
|  | #include "stdafx.h" | 
|  | #include "Example.h" | 
|  | #include <vector> | 
|  | #include <cstdio> | 
|  | #include <cassert> | 
|  | #include <fstream> | 
|  | #include <queue> | 
|  | #include <boost/optional.hpp> | 
|  | #include <WebGPU.h> | 
|  |  | 
|  | #define MAX_LOADSTRING 100 | 
|  |  | 
|  | // Global Variables: | 
|  | HINSTANCE hInst;                                // current instance | 
|  | WCHAR szTitle[MAX_LOADSTRING];                  // The title bar text | 
|  | WCHAR szWindowClass[MAX_LOADSTRING];            // the main window class name | 
|  |  | 
|  | HWND hWnd; | 
|  | std::unique_ptr<WebGPU::Device> device; | 
|  | WebGPU::Queue* commandQueue = nullptr; | 
|  | WebGPU::RenderState* renderState = nullptr; | 
|  | WebGPU::RenderState* renderStateRTT = nullptr; | 
|  | WebGPU::ComputeState* computeState = nullptr; | 
|  | std::unique_ptr<WebGPU::BufferHolder> buffer; | 
|  | std::unique_ptr<WebGPU::TextureHolder> texture; | 
|  | std::unique_ptr<WebGPU::BufferHolder> vertexBuffer; | 
|  | std::unique_ptr<WebGPU::BufferHolder> vertexBufferRTT; | 
|  | std::unique_ptr<WebGPU::SamplerHolder> sampler; | 
|  | std::queue<boost::unique_future<std::vector<uint8_t>>> futureQueue; | 
|  | bool initializedBuffers = false; | 
|  | float t = 0; | 
|  |  | 
|  | // Forward declarations of functions included in this code module: | 
|  | ATOM                MyRegisterClass(HINSTANCE hInstance); | 
|  | BOOL                InitInstance(HINSTANCE, int); | 
|  | LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM); | 
|  |  | 
|  | static std::vector<uint8_t> readFile(const std::string& filename) { | 
|  | std::ifstream file(filename.c_str(), std::ios::in | std::ios::binary | std::ios::ate); | 
|  | assert(file.is_open()); | 
|  | auto size = file.tellg(); | 
|  | std::vector<uint8_t> result(size); | 
|  | file.seekg(0, std::ios::beg); | 
|  | file.read(reinterpret_cast<char*>(result.data()), size); | 
|  | assert(file.tellg() == size); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | static void drawWebGPU() { | 
|  | if (!commandQueue || !renderState || !buffer || !vertexBuffer || !vertexBufferRTT || !renderStateRTT || !texture) | 
|  | return; | 
|  |  | 
|  | WINDOWINFO windowInfo; | 
|  | auto success = GetWindowInfo(hWnd, &windowInfo); | 
|  | assert(success); | 
|  | auto width = windowInfo.rcClient.right - windowInfo.rcClient.left; | 
|  | auto height = windowInfo.rcClient.bottom - windowInfo.rcClient.top; | 
|  |  | 
|  | while (!futureQueue.empty() && futureQueue.front().is_ready() && futureQueue.front().has_value()) { | 
|  | const auto& data = futureQueue.front().get(); | 
|  | float t = *reinterpret_cast<const float*>(data.data()); | 
|  | std::ostringstream ss; | 
|  | ss << "T has value " << t << std::endl; | 
|  | OutputDebugStringA(ss.str().c_str()); | 
|  | futureQueue.pop(); | 
|  | } | 
|  |  | 
|  | if (!initializedBuffers) { | 
|  | auto hostAccessPass = commandQueue->createHostAccessPass(); | 
|  |  | 
|  | std::vector<uint8_t> bufferContents(sizeof(float)); | 
|  | for (std::size_t i = 0; i < sizeof(float); ++i) | 
|  | bufferContents[i] = reinterpret_cast<uint8_t*>(&t)[i]; | 
|  | hostAccessPass->overwriteBuffer(buffer->get(), bufferContents); | 
|  |  | 
|  | std::vector<float> vertexBufferContents = { -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1 }; | 
|  | std::vector<uint8_t> vertexBufferRawContents(sizeof(float) * 12); | 
|  | for (std::size_t i = 0; i < vertexBufferContents.size(); ++i) { | 
|  | float value = vertexBufferContents[i]; | 
|  | for (std::size_t j = 0; j < sizeof(float); ++j) | 
|  | vertexBufferRawContents[sizeof(float) * i + j] = reinterpret_cast<uint8_t*>(&value)[j]; | 
|  | } | 
|  | hostAccessPass->overwriteBuffer(vertexBuffer->get(), vertexBufferRawContents); | 
|  |  | 
|  | std::vector<float> vertexBufferContentsRTT = { -0.5, 0.5, 0, -0.5, 0.5, 1.0 }; | 
|  | std::vector<uint8_t> vertexBufferRawContentsRTT(sizeof(float) * 6); | 
|  | for (std::size_t i = 0; i < vertexBufferContentsRTT.size(); ++i) { | 
|  | float value = vertexBufferContentsRTT[i]; | 
|  | for (std::size_t j = 0; j < sizeof(float); ++j) | 
|  | vertexBufferRawContentsRTT[sizeof(float) * i + j] = reinterpret_cast<uint8_t*>(&value)[j]; | 
|  | } | 
|  | hostAccessPass->overwriteBuffer(vertexBufferRTT->get(), vertexBufferRawContentsRTT); | 
|  | commandQueue->commitHostAccessPass(std::move(hostAccessPass)); | 
|  | initializedBuffers = true; | 
|  | } | 
|  |  | 
|  | auto computePass = commandQueue->createComputePass(); | 
|  | computePass->setComputeState(*computeState); | 
|  | computePass->setResources(0, { WebGPU::ShaderStorageBufferObjectReference(buffer->get()) }); | 
|  | computePass->dispatch(1, 1, 1); | 
|  | commandQueue->commitComputePass(std::move(computePass)); | 
|  |  | 
|  | std::vector<std::reference_wrapper<WebGPU::Texture>> rtt = { texture->get() }; | 
|  | auto renderPass = commandQueue->createRenderPass(&rtt); | 
|  | renderPass->setRenderState(*renderStateRTT); | 
|  | renderPass->setVertexAttributeBuffers({ vertexBufferRTT->get() }); | 
|  | renderPass->setResources(0, { WebGPU::UniformBufferObjectReference(buffer->get()) }); | 
|  | renderPass->setViewport(0, 0, width, height); | 
|  | renderPass->setScissorRect(0, 0, width, height); | 
|  | renderPass->draw(3); | 
|  | commandQueue->commitRenderPass(std::move(renderPass)); | 
|  |  | 
|  | auto renderPass2 = commandQueue->createRenderPass(nullptr); | 
|  | renderPass2->setRenderState(*renderState); | 
|  | renderPass2->setVertexAttributeBuffers({ vertexBuffer->get() }); | 
|  | renderPass2->setResources(0, { WebGPU::UniformBufferObjectReference(buffer->get()), WebGPU::TextureReference(texture->get()), WebGPU::SamplerReference(sampler->get()) }); | 
|  | renderPass2->setViewport(0, 0, width, height); | 
|  | renderPass2->setScissorRect(0, 0, width, height); | 
|  | renderPass2->draw(6); | 
|  | commandQueue->commitRenderPass(std::move(renderPass2)); | 
|  |  | 
|  | auto hostAccessPass = commandQueue->createHostAccessPass(); | 
|  | futureQueue.emplace(hostAccessPass->getBufferContents(buffer->get())); | 
|  | commandQueue->commitHostAccessPass(std::move(hostAccessPass)); | 
|  |  | 
|  | commandQueue->present(); | 
|  | } | 
|  |  | 
|  | int APIENTRY wWinMain(_In_ HINSTANCE hInstance, | 
|  | _In_opt_ HINSTANCE hPrevInstance, | 
|  | _In_ LPWSTR    lpCmdLine, | 
|  | _In_ int       nCmdShow) | 
|  | { | 
|  | UNREFERENCED_PARAMETER(hPrevInstance); | 
|  | UNREFERENCED_PARAMETER(lpCmdLine); | 
|  |  | 
|  | // TODO: Place code here. | 
|  |  | 
|  | // Initialize global strings | 
|  | LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); | 
|  | LoadStringW(hInstance, IDC_EXAMPLE, szWindowClass, MAX_LOADSTRING); | 
|  | MyRegisterClass(hInstance); | 
|  |  | 
|  | // Perform application initialization: | 
|  | if (!InitInstance (hInstance, nCmdShow)) | 
|  | { | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /* | 
|  | #version 460 | 
|  |  | 
|  | layout(location = 0) in vec2 position; | 
|  |  | 
|  | layout(std140, binding = 0) uniform buf { | 
|  | float t; | 
|  | }; | 
|  |  | 
|  | void main() { | 
|  | float time = t / 10; | 
|  | vec2 delta = vec2(cos(time), sin(time)); | 
|  | delta = delta * 0.5; | 
|  | gl_Position = vec4(position + delta, 0.5, 1); | 
|  | } | 
|  | glslangValidator -g -o "C:\Users\lithe\Documents\VertexShader.spv" -V -H "C:\Users\lithe\Documents\VertexShader.vert" -S vert | 
|  | #version 460 | 
|  |  | 
|  | layout (location = 0) out vec4 fragColor; | 
|  |  | 
|  | void main() { | 
|  | fragColor = vec4(1, 0, 0, 1); | 
|  | } | 
|  | glslangValidator -g -o "C:\Users\lithe\Documents\FragmentShader.spv" -V -H "C:\Users\lithe\Documents\FragmentShader.frag" -S frag | 
|  | #version 460 | 
|  |  | 
|  | layout(location = 0) in vec2 position; | 
|  |  | 
|  | layout(location = 0) out vec2 texCoord; | 
|  |  | 
|  | layout(std140, binding = 0) uniform buf { | 
|  | float t; | 
|  | }; | 
|  |  | 
|  | void main() { | 
|  | texCoord = position; | 
|  | gl_Position = vec4(position, 0.5, 1); | 
|  | } | 
|  | glslangValidator -g -o "C:\Users\lithe\Documents\VertexShader2.spv" -V -H "C:\Users\lithe\Documents\VertexShader2.vert" -S vert | 
|  | struct Result { | 
|  | float4 color : SV_Target0; | 
|  | }; | 
|  |  | 
|  | Texture2D<float4> inputTexture : register(t1); | 
|  | SamplerState inputSampler : register(s2); | 
|  |  | 
|  | Result main(float2 texCoord : TEXCOORD0) | 
|  | { | 
|  | Result result; | 
|  | float4 sampleResult = inputTexture.Sample(inputSampler, texCoord); | 
|  | result.color = float4(sampleResult.rgb, 1.0f); | 
|  | return result; | 
|  | } | 
|  | dxc.exe -spirv -T ps_6_0 "C:\Users\lithe\Documents\PixelShader.hlsl" -Fo "C:\Users\lithe\Documents\FragmentShader2.spv" | 
|  | */ | 
|  | commandQueue = &device->getCommandQueue(); | 
|  | auto vertexShaderRTT = readFile("C:\\Users\\lithe\\Documents\\VertexShader.spv"); | 
|  | auto fragmentShaderRTT = readFile("C:\\Users\\lithe\\Documents\\FragmentShader.spv"); | 
|  | auto computeShader = readFile("C:\\Users\\lithe\\Documents\\ComputeShader.spv"); | 
|  | auto vertexShader = readFile("C:\\Users\\lithe\\Documents\\VertexShader2.spv"); | 
|  | auto fragmentShader = readFile("C:\\Users\\lithe\\Documents\\FragmentShader2.spv"); | 
|  |  | 
|  | vertexBufferRTT = std::unique_ptr<WebGPU::BufferHolder>(new WebGPU::BufferHolder(device->getBuffer(static_cast<unsigned int>(sizeof(float) * 6)))); | 
|  | buffer = std::unique_ptr<WebGPU::BufferHolder>(new WebGPU::BufferHolder(device->getBuffer(static_cast<unsigned int>(sizeof(float))))); | 
|  | vertexBuffer = std::unique_ptr<WebGPU::BufferHolder>(new WebGPU::BufferHolder(device->getBuffer(static_cast<unsigned int>(sizeof(float) * 12)))); | 
|  |  | 
|  | sampler = std::unique_ptr<WebGPU::SamplerHolder>(new WebGPU::SamplerHolder(device->getSampler(WebGPU::AddressMode::Repeat, WebGPU::Filter::Linear))); | 
|  |  | 
|  | WINDOWINFO windowInfo; | 
|  | auto success = GetWindowInfo(hWnd, &windowInfo); | 
|  | assert(success); | 
|  | texture = std::unique_ptr<WebGPU::TextureHolder>(new WebGPU::TextureHolder(device->getTexture(windowInfo.rcClient.right - windowInfo.rcClient.left, windowInfo.rcClient.bottom - windowInfo.rcClient.top, WebGPU::PixelFormat::RGBA8))); | 
|  |  | 
|  | std::vector<WebGPU::RenderState::VertexAttribute> vertexAttributes = { {WebGPU::RenderState::VertexFormat::Float2, 0, 0} }; | 
|  | std::vector<WebGPU::PixelFormat> colorPixelFormats = { WebGPU::PixelFormat::RGBA8 }; | 
|  | renderStateRTT = &device->getRenderState(vertexShaderRTT, "main", fragmentShaderRTT, "main", { 8 }, vertexAttributes, { { WebGPU::ResourceType::UniformBufferObject } }, &colorPixelFormats); | 
|  | renderState = &device->getRenderState(vertexShader, "main", fragmentShader, "main", { 8 }, vertexAttributes, { { WebGPU::ResourceType::UniformBufferObject, WebGPU::ResourceType::Texture, WebGPU::ResourceType::Sampler } }, nullptr); | 
|  | computeState = &device->getComputeState(computeShader, "main", { { WebGPU::ResourceType::ShaderStorageBufferObject } }); | 
|  | drawWebGPU(); | 
|  |  | 
|  | HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_EXAMPLE)); | 
|  |  | 
|  | MSG msg; | 
|  |  | 
|  | // Main message loop: | 
|  | while (GetMessage(&msg, nullptr, 0, 0)) | 
|  | { | 
|  | if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) | 
|  | { | 
|  | TranslateMessage(&msg); | 
|  | DispatchMessage(&msg); | 
|  | } | 
|  | RedrawWindow(hWnd, nullptr, nullptr, RDW_INTERNALPAINT); | 
|  | } | 
|  | return (int) msg.wParam; | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | // | 
|  | //  FUNCTION: MyRegisterClass() | 
|  | // | 
|  | //  PURPOSE: Registers the window class. | 
|  | // | 
|  | ATOM MyRegisterClass(HINSTANCE hInstance) | 
|  | { | 
|  | WNDCLASSEXW wcex; | 
|  |  | 
|  | wcex.cbSize = sizeof(WNDCLASSEX); | 
|  |  | 
|  | wcex.style          = CS_HREDRAW | CS_VREDRAW; | 
|  | wcex.lpfnWndProc    = WndProc; | 
|  | wcex.cbClsExtra     = 0; | 
|  | wcex.cbWndExtra     = 0; | 
|  | wcex.hInstance      = hInstance; | 
|  | wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_EXAMPLE)); | 
|  | wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW); | 
|  | wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1); | 
|  | wcex.lpszMenuName   = nullptr; | 
|  | wcex.lpszClassName  = szWindowClass; | 
|  | wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); | 
|  |  | 
|  | return RegisterClassExW(&wcex); | 
|  | } | 
|  |  | 
|  | // | 
|  | //   FUNCTION: InitInstance(HINSTANCE, int) | 
|  | // | 
|  | //   PURPOSE: Saves instance handle and creates main window | 
|  | // | 
|  | //   COMMENTS: | 
|  | // | 
|  | //        In this function, we save the instance handle in a global variable and | 
|  | //        create and display the main program window. | 
|  | // | 
|  | BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) | 
|  | { | 
|  | hInst = hInstance; // Store instance handle in our global variable | 
|  |  | 
|  | hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, | 
|  | CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr); | 
|  |  | 
|  | if (!hWnd) | 
|  | { | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | device = WebGPU::Device::create(hInstance, hWnd); | 
|  |  | 
|  | ShowWindow(hWnd, nCmdShow); | 
|  | UpdateWindow(hWnd); | 
|  |  | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | // | 
|  | //  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM) | 
|  | // | 
|  | //  PURPOSE:  Processes messages for the main window. | 
|  | // | 
|  | //  WM_COMMAND  - process the application menu | 
|  | //  WM_PAINT    - Paint the main window | 
|  | //  WM_DESTROY  - post a quit message and return | 
|  | // | 
|  | // | 
|  | LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) | 
|  | { | 
|  | switch (message) | 
|  | { | 
|  | case WM_PAINT: | 
|  | drawWebGPU(); | 
|  | break; | 
|  | case WM_DESTROY: | 
|  | PostQuitMessage(0); | 
|  | break; | 
|  | default: | 
|  | return DefWindowProc(hWnd, message, wParam, lParam); | 
|  | } | 
|  | return 0; | 
|  | } |