203: Add compute example r=kvark a=Napokue



Co-authored-by: Timo de Kort <dekort.timo@gmail.com>
This commit is contained in:
bors[bot]
2019-07-29 19:38:56 +00:00
14 changed files with 294 additions and 38 deletions

View File

@@ -70,11 +70,14 @@ lib-remote: Cargo.lock wgpu-remote/Cargo.toml $(wildcard wgpu-native/**/*.rs wgp
ffi/wgpu.h: wgpu-native/cbindgen.toml $(wildcard wgpu-native/**/*.rs)
rustup run nightly cbindgen wgpu-native > $(FFI_DIR)/wgpu.h
ffi/wgpu-remote.h: wgpu-remote/cbindgen.toml $(wildcard wgpu-native/**/*.rs wgpu-remote/**/*.rs)
rustup run nightly cbindgen wgpu-remote >$(FFI_DIR)/wgpu-remote.h
ffi/wgpu-remote.h: wgpu-remote/cbindgen.toml $(wildcard wgpu-native/**/*.rs wgpu-remote/**/*.rs)
rustup run nightly cbindgen wgpu-remote > $(FFI_DIR)/wgpu-remote.h
examples-native: lib-native $(FFI_DIR)/wgpu.h examples/hello_triangle_c/main.c
cd examples/hello_triangle_c && $(CREATE_BUILD_DIR) && cd build && cmake .. -DBACKEND=$(FEATURE_RUST) $(GENERATOR_PLATFORM) && cmake --build .
example-compute: lib-native $(FFI_DIR)/wgpu.h examples/compute/main.c
cd examples/compute && $(CREATE_BUILD_DIR) && cd build && cmake .. -DBACKEND=$(FEATURE_RUST) $(GENERATOR_PLATFORM) && cmake --build .
examples-remote: lib-remote $(FFI_DIR)/wgpu-remote.h examples/hello_remote_c/main.c
cd examples/hello_remote_c && $(CREATE_BUILD_DIR) && cd build && cmake .. && cmake --build .
example-triangle: lib-native $(FFI_DIR)/wgpu.h examples/triangle/main.c
cd examples/triangle && $(CREATE_BUILD_DIR) && cd build && cmake .. -DBACKEND=$(FEATURE_RUST) $(GENERATOR_PLATFORM) && cmake --build .
example-remote: lib-remote $(FFI_DIR)/wgpu-remote.h examples/remote/main.c
cd examples/remote && $(CREATE_BUILD_DIR) && cd build && cmake .. && cmake --build .

View File

@@ -20,14 +20,37 @@ Supported platforms:
## Usage
This repository contains C-language examples that link to the native library targets and perform basic rendering and computation.
To run the C triangle example, install a C compiler + glfw 3, then run these commands at the root of the repo:
```
rustup toolchain install nightly
cargo install cbindgen
make examples-native
cd examples/hello_triangle_c/build
./hello_triangle
This repository contains C-language examples that link to the native library targets and perform basic rendering and computation.
### Prerequisites
- C compiler
- GLFW 3
- Rust nightly toolchain
```bash
rustup toolchain install nightly
```
- [Cbindgen](https://github.com/eqrion/cbindgen)
```bash
cargo install cbindgen
```
### Running an example
The examples are located under the [examples](examples) directory. We are using a [Makefile](Makefile) for running the examples.
#### Triangle
```bash
make example-triangle
```
The idiomatic Rust wrapper lives in https://github.com/gfx-rs/wgpu-rs and provides a number of more complex examples to get a feel of the API.
#### Compute
```bash
make example-compute
```
#### Remote
```bash
make example-remote
```
The idiomatic Rust wrapper lives in https://github.com/gfx-rs/wgpu-rs and provides a number of more [complex examples](https://github.com/gfx-rs/wgpu-rs/tree/master/examples) to get a feel of the API.

View File

@@ -14,11 +14,11 @@ if(NOT DEFINED BACKEND OR NOT "${BACKEND}" IN_LIST AVAILABLE_BACKENDS)
message(FATAL_ERROR "BACKEND invalid or undefined, available backends: ${AVAILABLE_BACKENDS}")
endif()
project(hello_triangle)
project(compute)
set(TARGET_NAME hello_triangle)
set(TARGET_NAME compute)
add_executable(hello_triangle main.c)
add_executable(compute main.c ../framework.c)
if(MSVC)
add_definitions(-DWGPU_TARGET=WGPU_TARGET_WINDOWS)
@@ -47,5 +47,7 @@ find_library(WGPU_LIBRARY wgpu_native
)
target_include_directories(${TARGET_NAME} PUBLIC $ENV{GLFW3_INCLUDE_DIR})
target_include_directories(${TARGET_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../ffi)
target_include_directories(${TARGET_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../)
target_link_libraries(${TARGET_NAME} glfw ${WGPU_LIBRARY} ${OS_LIBRARIES})

138
examples/compute/main.c Normal file
View File

@@ -0,0 +1,138 @@
#ifndef WGPU_H
#define WGPU_H
#include "wgpu.h"
#endif
#include "framework.h"
#include <stdio.h>
#include <stdlib.h>
#define BINDINGS_LENGTH (1)
#define BIND_GROUP_LAYOUTS_LENGTH (1)
int main(
int argc,
char *argv[]) {
if (argc != 5) {
printf("You must pass 4 positive integers!");
return 0;
}
uint32_t numbers[] = {
strtoul(argv[1], NULL, 0),
strtoul(argv[2], NULL, 0),
strtoul(argv[3], NULL, 0),
strtoul(argv[4], NULL, 0),
};
uint32_t size = sizeof(numbers);
uint32_t numbers_length = size / sizeof(uint32_t);
WGPUInstanceId instance = wgpu_create_instance();
WGPUAdapterId adapter = wgpu_instance_get_adapter(instance,
&(WGPUAdapterDescriptor){
.power_preference = WGPUPowerPreference_LowPower,
});
WGPUDeviceId device = wgpu_adapter_request_device(adapter,
&(WGPUDeviceDescriptor){
.extensions = NULL
});
uint8_t *staging_memory;
WGPUBufferId staging_buffer = wgpu_device_create_buffer_mapped(device,
&(WGPUBufferDescriptor){
.size = size,
.usage = WGPUBufferUsage_MAP_READ},
&staging_memory);
memcpy((uint32_t *) staging_memory, numbers, size);
wgpu_buffer_unmap(staging_buffer);
WGPUBufferId storage_buffer = wgpu_device_create_buffer(device,
&(WGPUBufferDescriptor){
.size = size,
.usage = WGPUBufferUsage_STORAGE});
WGPUBindGroupLayoutId bind_group_layout =
wgpu_device_create_bind_group_layout(device,
&(WGPUBindGroupLayoutDescriptor){
.bindings = &(WGPUBindGroupLayoutBinding){
.binding = 0,
.visibility = WGPUShaderStage_COMPUTE,
.ty = WGPUBindingType_StorageBuffer},
.bindings_length = BINDINGS_LENGTH});
WGPUBindingResource resource = {
.tag = WGPUBindingResource_Buffer,
.buffer = (WGPUBufferBinding){
.buffer = storage_buffer,
.size = size,
.offset = 0}};
WGPUBindGroupId bind_group = wgpu_device_create_bind_group(device,
&(WGPUBindGroupDescriptor){.layout = bind_group_layout,
.bindings = &(WGPUBindGroupBinding){
.binding = 0,
.resource = resource},
.bindings_length = BINDINGS_LENGTH});
WGPUBindGroupLayoutId bind_group_layouts[BIND_GROUP_LAYOUTS_LENGTH] = {
bind_group_layout};
WGPUPipelineLayoutId pipeline_layout =
wgpu_device_create_pipeline_layout(device,
&(WGPUPipelineLayoutDescriptor){
.bind_group_layouts = bind_group_layouts,
.bind_group_layouts_length = BIND_GROUP_LAYOUTS_LENGTH});
WGPUShaderModuleId shader_module = wgpu_device_create_shader_module(device,
&(WGPUShaderModuleDescriptor){
.code = read_file("./../../data/collatz.comp.spv")});
WGPUComputePipelineId compute_pipeline =
wgpu_device_create_compute_pipeline(device,
&(WGPUComputePipelineDescriptor){
.layout = pipeline_layout,
.compute_stage = (WGPUPipelineStageDescriptor){
.module = shader_module,
.entry_point = "main"
}});
WGPUCommandEncoderId encoder = wgpu_device_create_command_encoder(
device, &(WGPUCommandEncoderDescriptor){
.todo = 0
});
wgpu_command_buffer_copy_buffer_to_buffer(
encoder, staging_buffer, 0, storage_buffer, 0, size);
WGPUComputePassId command_pass =
wgpu_command_encoder_begin_compute_pass(encoder);
wgpu_compute_pass_set_pipeline(command_pass, compute_pipeline);
wgpu_compute_pass_set_bind_group(command_pass, 0, bind_group, NULL, 0);
wgpu_compute_pass_dispatch(command_pass, numbers_length, 1, 1);
wgpu_compute_pass_end_pass(command_pass);
wgpu_command_buffer_copy_buffer_to_buffer(
encoder, storage_buffer, 0, staging_buffer, 0, size);
WGPUQueueId queue = wgpu_device_get_queue(device);
WGPUCommandBufferId command_buffer = wgpu_command_encoder_finish(encoder);
wgpu_queue_submit(queue, &command_buffer, 1);
wgpu_buffer_map_read_async(staging_buffer, 0, size, read_buffer_map, NULL);
wgpu_device_poll(device, true);
return 0;
}

35
examples/framework.c Normal file
View File

@@ -0,0 +1,35 @@
#ifndef WGPU_H
#define WGPU_H
#include "wgpu.h"
#endif
#include <stdio.h>
#include <stdlib.h>
WGPUByteArray read_file(const char *name) {
FILE *file = fopen(name, "rb");
fseek(file, 0, SEEK_END);
long length = ftell(file);
unsigned char *bytes = malloc(length);
fseek(file, 0, SEEK_SET);
fread(bytes, 1, length, file);
fclose(file);
return (WGPUByteArray){
.bytes = bytes,
.length = length,
};
}
void read_buffer_map(
WGPUBufferMapAsyncStatus status,
const uint8_t *data,
uint8_t *userdata) {
if (status == WGPUBufferMapAsyncStatus_Success) {
uint32_t *times = (uint32_t *) data;
printf("Times: [%d, %d, %d, %d]",
times[0],
times[1],
times[2],
times[3]);
}
}

11
examples/framework.h Normal file
View File

@@ -0,0 +1,11 @@
#ifndef WGPU_H
#define WGPU_H
#include "wgpu.h"
#endif
WGPUByteArray read_file(const char *name);
void read_buffer_map(
WGPUBufferMapAsyncStatus status,
const uint8_t *data,
uint8_t *userdata);

View File

@@ -1,10 +1,10 @@
cmake_minimum_required(VERSION 3.11b)
project(hello_remote)
project(remote)
set(TARGET_NAME hello_remote)
set(TARGET_NAME remote)
add_executable(hello_remote main.c)
add_executable(remote main.c)
find_package(glfw3)

View File

@@ -0,0 +1,53 @@
cmake_minimum_required(VERSION 3.11b)
set(BACKEND_VULKAN "vulkan")
set(BACKEND_METAL "metal")
set(BACKEND_DX11 "dx11")
set(BACKEND_DX12 "dx12")
set(AVAILABLE_BACKENDS
${BACKEND_VULKAN}
${BACKEND_METAL}
${BACKEND_DX11}
${BACKEND_DX12})
if(NOT DEFINED BACKEND OR NOT "${BACKEND}" IN_LIST AVAILABLE_BACKENDS)
message(FATAL_ERROR "BACKEND invalid or undefined, available backends: ${AVAILABLE_BACKENDS}")
endif()
project(triangle)
set(TARGET_NAME triangle)
add_executable(triangle main.c ../framework.c)
if(MSVC)
add_definitions(-DWGPU_TARGET=WGPU_TARGET_WINDOWS)
target_compile_options(${TARGET_NAME} PRIVATE /W4)
set(OS_LIBRARIES "userenv" "ws2_32" "Dwmapi" "dbghelp")
if("${BACKEND}" STREQUAL "${BACKEND_DX11}")
list(APPEND OS_LIBRARIES "d3dcompiler" "D3D11" "DXGI")
elseif("${BACKEND}" STREQUAL "${BACKEND_DX12}")
list(APPEND OS_LIBRARIES "d3dcompiler" "D3D12" "DXGI")
endif()
elseif(APPLE)
add_definitions(-DWGPU_TARGET=WGPU_TARGET_MACOS)
set(OS_LIBRARIES "-framework Cocoa" "-framework CoreVideo" "-framework IOKit" "-framework QuartzCore")
target_compile_options(${TARGET_NAME} PRIVATE -x objective-c)
else(MSVC)
add_definitions(-DWGPU_TARGET=WGPU_TARGET_LINUX)
target_compile_options(${TARGET_NAME} PRIVATE -Wall -Wextra -pedantic)
endif(MSVC)
find_package(glfw3 3.3 REQUIRED
HINTS "$ENV{GLFW3_INSTALL_DIR}"
)
find_library(WGPU_LIBRARY wgpu_native
HINTS "${CMAKE_CURRENT_SOURCE_DIR}/../../target/debug"
)
target_include_directories(${TARGET_NAME} PUBLIC $ENV{GLFW3_INCLUDE_DIR})
target_include_directories(${TARGET_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../ffi)
target_include_directories(${TARGET_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../)
target_link_libraries(${TARGET_NAME} glfw ${WGPU_LIBRARY} ${OS_LIBRARIES})

View File

@@ -1,4 +1,9 @@
#include "./../../ffi/wgpu.h"
#ifndef WGPU_H
#define WGPU_H
#include "wgpu.h"
#endif
#include "framework.h"
#include <stdio.h>
#include <stdlib.h>
@@ -27,20 +32,6 @@
#define RENDER_PASS_ATTACHMENTS_LENGTH (1)
#define BIND_GROUP_LAYOUTS_LENGTH (1)
WGPUByteArray read_file(const char *name) {
FILE *file = fopen(name, "rb");
fseek(file, 0, SEEK_END);
long length = ftell(file);
unsigned char *bytes = malloc(length);
fseek(file, 0, SEEK_SET);
fread(bytes, 1, length, file);
fclose(file);
return (WGPUByteArray){
.bytes = bytes,
.length = length,
};
}
int main() {
WGPUInstanceId instance = wgpu_create_instance();
@@ -59,13 +50,13 @@ int main() {
WGPUShaderModuleId vertex_shader = wgpu_device_create_shader_module(device,
&(WGPUShaderModuleDescriptor){
.code = read_file("./../../data/hello_triangle.vert.spv"),
.code = read_file("./../../data/triangle.vert.spv"),
});
WGPUShaderModuleId fragment_shader =
wgpu_device_create_shader_module(device,
&(WGPUShaderModuleDescriptor){
.code = read_file("./../../data/hello_triangle.frag.spv"),
.code = read_file("./../../data/triangle.frag.spv"),
});
WGPUBindGroupLayoutId bind_group_layout =