Rework ManualSwapChainTest into ManualSurfaceTest
Bug: 42241264
Change-Id: I5d6f6563baca38bb58a1cbae3fef2893b8395af1
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/187141
Reviewed-by: Loko Kung <lokokung@google.com>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
diff --git a/src/dawn/samples/BUILD.gn b/src/dawn/samples/BUILD.gn
index bb75243..3cbadb5 100644
--- a/src/dawn/samples/BUILD.gn
+++ b/src/dawn/samples/BUILD.gn
@@ -33,7 +33,7 @@
":ComputeBoids",
":DawnInfo",
":HelloTriangle",
- ":ManualSwapChainTest",
+ ":ManualSurfaceTest",
]
}
@@ -78,8 +78,8 @@
sources = [ "Animometer.cpp" ]
}
-sample("ManualSwapChainTest") {
- sources = [ "ManualSwapChainTest.cpp" ]
+sample("ManualSurfaceTest") {
+ sources = [ "ManualSurfaceTest.cpp" ]
}
sample("DawnInfo") {
diff --git a/src/dawn/samples/CMakeLists.txt b/src/dawn/samples/CMakeLists.txt
index 5ae9f65..f2caafd 100644
--- a/src/dawn/samples/CMakeLists.txt
+++ b/src/dawn/samples/CMakeLists.txt
@@ -59,3 +59,6 @@
common_compile_options(DawnInfo)
target_link_libraries(DawnInfo dawn_sample_utils)
+add_executable(ManualSurfaceTest "ManualSurfaceTest.cpp")
+common_compile_options(ManualSurfaceTest)
+target_link_libraries(ManualSurfaceTest dawn_sample_utils)
diff --git a/src/dawn/samples/ManualSwapChainTest.cpp b/src/dawn/samples/ManualSurfaceTest.cpp
similarity index 64%
rename from src/dawn/samples/ManualSwapChainTest.cpp
rename to src/dawn/samples/ManualSurfaceTest.cpp
index 1ac688c..c9ef652 100644
--- a/src/dawn/samples/ManualSwapChainTest.cpp
+++ b/src/dawn/samples/ManualSurfaceTest.cpp
@@ -25,7 +25,7 @@
// 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.
-// This is an example to manually test swapchain code. Controls are the following, scoped to the
+// This is an example to manually test surface code. Controls are the following, scoped to the
// currently focused window:
// - W: creates a new window.
// - L: Latches the current swapchain, to check what happens when the window changes but not the
@@ -34,6 +34,8 @@
// (WARNING) likely seizure inducing.
// - D: cycles the divisor for the swapchain size.
// - P: switches present modes.
+// - A: switches alpha modes.
+// - F: switches formats.
//
// Closing all the windows exits the example. ^C also works.
//
@@ -60,12 +62,24 @@
// diagonal aspect ratio).
//
// - Config change tests:
-// - Check that cycling between present modes works.
-// - TODO can't be tested yet: check cycling the same window over multiple devices.
-// - TODO can't be tested yet: check cycling the same window over multiple formats.
+// - Check that cycling between present modes.
+// - Check that cycling between alpha modes (it sometimes produce a meaningful difference).
+// - Check that cycling between formats works and gives the same color.
+//
+// - Frame throttling:
+// - In all present modes, check that there isn't extra latency when going from a render mode
+// to another (like from the color cycling one to the triangle).
+//
+// - Additional things to test that aren't supported in this file yet.
+// - Check cycling the same window over multiple devices.
+// - Check sRGB vs not sRGB gradients.
+// - Check wide gamut / extended color range.
+// - Check OpenGL rendering with extra usages / depth buffer / MRT.
+// - Check with GLFW transparency on / off.
#include <algorithm>
#include <memory>
+#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
@@ -78,8 +92,29 @@
#include "dawn/utils/ComboRenderPipelineDescriptor.h"
#include "dawn/utils/WGPUHelpers.h"
#include "dawn/webgpu_cpp.h"
+#include "dawn/webgpu_cpp_print.h"
#include "webgpu/webgpu_glfw.h"
+template <typename T>
+std::string NoPrefix(T wgpuThing) {
+ std::ostringstream o;
+ o << wgpuThing;
+ std::string withPrefix = o.str();
+ return withPrefix.substr(withPrefix.rfind(':') + 1);
+}
+
+template <typename T>
+void CycleIn(T* value, const std::vector<T>& cycle) {
+ auto it = std::find(cycle.begin(), cycle.end(), *value);
+ DAWN_ASSERT(it != cycle.end());
+ it++;
+ if (it != cycle.end()) {
+ *value = *it;
+ } else {
+ *value = cycle.front();
+ }
+}
+
struct WindowData {
GLFWwindow* window = nullptr;
uint64_t serial = 0;
@@ -89,24 +124,64 @@
bool renderTriangle = true;
uint32_t divisor = 1;
- wgpu::Surface surface = nullptr;
- wgpu::SwapChain swapchain = nullptr;
+ std::vector<wgpu::PresentMode> presentModes;
+ std::vector<wgpu::CompositeAlphaMode> alphaModes;
+ std::vector<wgpu::TextureFormat> formats;
- wgpu::SwapChainDescriptor currentDesc;
- wgpu::SwapChainDescriptor targetDesc;
+ wgpu::Surface surface = nullptr;
+ wgpu::SurfaceConfiguration currentConfig;
+ wgpu::SurfaceConfiguration targetConfig;
};
static std::unordered_map<GLFWwindow*, std::unique_ptr<WindowData>> windows;
static uint64_t windowSerial = 0;
static std::unique_ptr<dawn::native::Instance> instance;
+static wgpu::Adapter adapter;
static wgpu::Device device;
static wgpu::Queue queue;
-static wgpu::RenderPipeline trianglePipeline;
-bool IsSameDescriptor(const wgpu::SwapChainDescriptor& a, const wgpu::SwapChainDescriptor& b) {
- return a.usage == b.usage && a.format == b.format && a.width == b.width &&
- a.height == b.height && a.presentMode == b.presentMode;
+static std::unordered_map<wgpu::TextureFormat, wgpu::RenderPipeline> trianglePipelines;
+wgpu::RenderPipeline GetOrCreateTrianglePipeline(wgpu::TextureFormat format) {
+ if (trianglePipelines.count(format)) {
+ return trianglePipelines[format];
+ }
+
+ // The hacky pipeline to render a triangle.
+ wgpu::ShaderModule module = dawn::utils::CreateShaderModule(device, R"(
+ @vertex fn vs(@builtin(vertex_index) VertexIndex : u32)
+ -> @builtin(position) vec4f {
+ var pos = array(
+ vec2f( 0.0, 0.5),
+ vec2f(-0.5, -0.5),
+ vec2f( 0.5, -0.5)
+ );
+ return vec4f(pos[VertexIndex], 0, 1);
+ }
+
+ @fragment fn fs() -> @location(0) vec4f {
+ return vec4f(1, 0, 0, 1);
+ }
+ )");
+
+ dawn::utils::ComboRenderPipelineDescriptor pipelineDesc;
+ pipelineDesc.vertex.module = module;
+ pipelineDesc.cFragment.module = module;
+ pipelineDesc.cTargets[0].format = format;
+ trianglePipelines[format] = device.CreateRenderPipeline(&pipelineDesc);
+ return trianglePipelines[format];
+}
+
+bool IsSameConfig(const wgpu::SurfaceConfiguration& a, const wgpu::SurfaceConfiguration& b) {
+ DAWN_ASSERT(a.viewFormatCount == 0);
+ DAWN_ASSERT(b.viewFormatCount == 0);
+ return a.device.Get() == b.device.Get() && //
+ a.format == b.format && //
+ a.usage == b.usage && //
+ a.alphaMode == b.alphaMode && //
+ a.width == b.width && //
+ a.height == b.height && //
+ a.presentMode == b.presentMode;
}
void OnKeyPress(GLFWwindow* window, int key, int, int action, int);
@@ -116,8 +191,8 @@
int height;
glfwGetFramebufferSize(data->window, &width, &height);
- data->targetDesc.width = std::max(1u, width / data->divisor);
- data->targetDesc.height = std::max(1u, height / data->divisor);
+ data->targetConfig.width = std::max(1u, width / data->divisor);
+ data->targetConfig.height = std::max(1u, height / data->divisor);
}
void AddWindow() {
@@ -125,36 +200,48 @@
GLFWwindow* window = glfwCreateWindow(400, 400, "", nullptr, nullptr);
glfwSetKeyCallback(window, OnKeyPress);
- wgpu::SwapChainDescriptor descriptor;
- descriptor.usage = wgpu::TextureUsage::RenderAttachment;
- descriptor.format = wgpu::TextureFormat::BGRA8Unorm;
- descriptor.width = 0;
- descriptor.height = 0;
- descriptor.presentMode = wgpu::PresentMode::Fifo;
+ wgpu::Surface surface = wgpu::glfw::CreateSurfaceForWindow(instance->Get(), window);
+ wgpu::SurfaceCapabilities caps;
+ surface.GetCapabilities(adapter, &caps);
+
+ wgpu::SurfaceConfiguration config;
+ config.device = device;
+ config.usage = wgpu::TextureUsage::RenderAttachment;
+ config.format = caps.formats[0];
+ config.alphaMode = caps.alphaModes[0];
+ config.presentMode = caps.presentModes[0];
+ config.width = 0;
+ config.height = 0;
std::unique_ptr<WindowData> data = std::make_unique<WindowData>();
data->window = window;
data->serial = windowSerial++;
- data->surface = wgpu::glfw::CreateSurfaceForWindow(instance->Get(), window);
- data->currentDesc = descriptor;
- data->targetDesc = descriptor;
+ data->surface = surface;
+ data->currentConfig = config;
+ data->targetConfig = config;
SyncFromWindow(data.get());
+ data->presentModes.assign(caps.presentModes, caps.presentModes + caps.presentModeCount);
+ data->alphaModes.assign(caps.alphaModes, caps.alphaModes + caps.alphaModeCount);
+ data->formats.assign(caps.formats, caps.formats + caps.formatCount);
windows[window] = std::move(data);
}
void DoRender(WindowData* data) {
- wgpu::TextureView view = data->swapchain.GetCurrentTextureView();
+ wgpu::SurfaceTexture surfaceTexture;
+ data->surface.GetCurrentTexture(&surfaceTexture);
+ wgpu::TextureView view = surfaceTexture.texture.CreateView();
+
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
if (data->renderTriangle) {
dawn::utils::ComboRenderPassDescriptor desc({view});
- // Use Load to check the swapchain is lazy cleared (we shouldn't see garbage from previous
+ // Use Load to check the surface is lazy cleared (we shouldn't see garbage from previous
// frames).
desc.cColorAttachments[0].loadOp = wgpu::LoadOp::Load;
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&desc);
- pass.SetPipeline(trianglePipeline);
+ pass.SetPipeline(GetOrCreateTrianglePipeline(data->currentConfig.format));
pass.Draw(3);
pass.End();
} else {
@@ -175,30 +262,18 @@
wgpu::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands);
- data->swapchain.Present();
+ data->surface.Present();
}
-std::ostream& operator<<(std::ostream& o, const wgpu::SwapChainDescriptor& desc) {
+std::ostream& operator<<(std::ostream& o, const wgpu::SurfaceConfiguration& desc) {
// For now only render attachment is possible.
DAWN_ASSERT(desc.usage == wgpu::TextureUsage::RenderAttachment);
o << "RenderAttachment ";
+
o << desc.width << "x" << desc.height << " ";
-
- // For now only BGRA is allowed
- DAWN_ASSERT(desc.format == wgpu::TextureFormat::BGRA8Unorm);
- o << "BGRA8Unorm ";
-
- switch (desc.presentMode) {
- case wgpu::PresentMode::Immediate:
- o << "Immediate";
- break;
- case wgpu::PresentMode::Fifo:
- o << "Fifo";
- break;
- case wgpu::PresentMode::Mailbox:
- o << "Mailbox";
- break;
- }
+ o << NoPrefix(desc.format) << " ";
+ o << NoPrefix(desc.presentMode) << " ";
+ o << NoPrefix(desc.alphaMode) << " ";
return o;
}
@@ -211,10 +286,10 @@
}
if (data->latched) {
- o << "Latched: (" << data->currentDesc << ") ";
- o << "Target: (" << data->targetDesc << ")";
+ o << "Latched: (" << data->currentConfig << ") ";
+ o << "Target: (" << data->targetConfig << ")";
} else {
- o << "(" << data->currentDesc << ")";
+ o << "(" << data->currentConfig << ")";
}
glfwSetWindowTitle(data->window, o.str().c_str());
@@ -251,17 +326,15 @@
break;
case GLFW_KEY_P:
- switch (data->targetDesc.presentMode) {
- case wgpu::PresentMode::Immediate:
- data->targetDesc.presentMode = wgpu::PresentMode::Fifo;
- break;
- case wgpu::PresentMode::Fifo:
- data->targetDesc.presentMode = wgpu::PresentMode::Mailbox;
- break;
- case wgpu::PresentMode::Mailbox:
- data->targetDesc.presentMode = wgpu::PresentMode::Immediate;
- break;
- }
+ CycleIn(&data->targetConfig.presentMode, data->presentModes);
+ break;
+
+ case GLFW_KEY_A:
+ CycleIn(&data->targetConfig.alphaMode, data->alphaModes);
+ break;
+
+ case GLFW_KEY_F:
+ CycleIn(&data->targetConfig.format, data->formats);
break;
default:
@@ -287,6 +360,7 @@
dawn::native::Adapter chosenAdapter = instance->EnumerateAdapters()[0];
DAWN_ASSERT(chosenAdapter);
+ adapter = wgpu::Adapter(chosenAdapter.Get());
// Setup the device on that adapter.
device = wgpu::Device::Acquire(chosenAdapter.CreateDevice());
@@ -311,31 +385,12 @@
return;
}
dawn::ErrorLog() << errorTypeName << " error: " << message;
+ DAWN_ASSERT(false);
},
nullptr);
queue = device.GetQueue();
- // The hacky pipeline to render a triangle.
- dawn::utils::ComboRenderPipelineDescriptor pipelineDesc;
- pipelineDesc.vertex.module = dawn::utils::CreateShaderModule(device, R"(
- @vertex fn main(@builtin(vertex_index) VertexIndex : u32)
- -> @builtin(position) vec4f {
- var pos = array(
- vec2f( 0.0, 0.5),
- vec2f(-0.5, -0.5),
- vec2f( 0.5, -0.5)
- );
- return vec4f(pos[VertexIndex], 0.0, 1.0);
- })");
- pipelineDesc.cFragment.module = dawn::utils::CreateShaderModule(device, R"(
- @fragment fn main() -> @location(0) vec4f {
- return vec4f(1.0, 0.0, 0.0, 1.0);
- })");
- // BGRA shouldn't be hardcoded. Consider having a map[format -> pipeline].
- pipelineDesc.cTargets[0].format = wgpu::TextureFormat::BGRA8Unorm;
- trianglePipeline = device.CreateRenderPipeline(&pipelineDesc);
-
- // Craete the first window, since the example exits when there are no windows.
+ // Create the first window, since the example exits when there are no windows.
AddWindow();
while (windows.size() != 0) {
@@ -357,9 +412,9 @@
WindowData* data = it.second.get();
SyncFromWindow(data);
- if (!IsSameDescriptor(data->currentDesc, data->targetDesc) && !data->latched) {
- data->swapchain = device.CreateSwapChain(data->surface, &data->targetDesc);
- data->currentDesc = data->targetDesc;
+ if (!IsSameConfig(data->currentConfig, data->targetConfig) && !data->latched) {
+ data->surface.Configure(&data->targetConfig);
+ data->currentConfig = data->targetConfig;
}
UpdateTitle(data);
DoRender(data);