Commit 37c768e9 authored by Kai Ninomiya's avatar Kai Ninomiya Committed by Commit Bot

Update naming for vertex state

Specification change:
https://github.com/gpuweb/gpuweb/pull/469

Dawn roll:
https://dawn.googlesource.com/dawn/+log/c3284fa40ec6b12731ed66c2f2a9256ae3fa692e..ae1f25fee85ebf2773b24a0a4f39c160839b1dbb

CTS roll:
https://github.com/gpuweb/cts/compare/3dc37c83a70667e9a92df773f34d154ec600d203...e114192747a54f34157eb65754e037701fbdf98b

TBR: haraken@chromium.org
Bug: dawn:22
Change-Id: Ib427760b9fba4b2cc4290de046c4a31d77e0b67a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1900807Reviewed-by: default avatarAustin Eng <enga@chromium.org>
Reviewed-by: default avatarYunchao He <yunchao.he@intel.com>
Reviewed-by: default avatarCorentin Wallez <cwallez@chromium.org>
Commit-Queue: Kai Ninomiya <kainino@chromium.org>
Cr-Commit-Position: refs/heads/master@{#713682}
parent 38512b7a
......@@ -307,7 +307,7 @@ vars = {
# Three lines of non-changing comments so that
# the commit queue can handle CLs rolling feed
# and whatever else without interference from each other.
'dawn_revision': 'c3284fa40ec6b12731ed66c2f2a9256ae3fa692e',
'dawn_revision': 'ae1f25fee85ebf2773b24a0a4f39c160839b1dbb',
# Three lines of non-changing comments so that
# the commit queue can handle CLs rolling feed
# and whatever else without interference from each other.
......
......@@ -867,8 +867,8 @@ modules_dictionary_idl_files =
"webgpu/gpu_texture_view_descriptor.idl",
"webgpu/gpu_uncaptured_error_event_init.idl",
"webgpu/gpu_vertex_attribute_descriptor.idl",
"webgpu/gpu_vertex_buffer_descriptor.idl",
"webgpu/gpu_vertex_input_descriptor.idl",
"webgpu/gpu_vertex_buffer_layout_descriptor.idl",
"webgpu/gpu_vertex_state_descriptor.idl",
"webmidi/midi_connection_event_init.idl",
"webmidi/midi_message_event_init.idl",
"webmidi/midi_options.idl",
......
......@@ -4,7 +4,7 @@
#include "third_party/blink/renderer/modules/webgpu/gpu_render_pipeline.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_vertex_buffer_descriptor.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_vertex_buffer_layout_descriptor.h"
#include "third_party/blink/renderer/modules/webgpu/dawn_conversions.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_blend_descriptor.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_color_state_descriptor.h"
......@@ -12,8 +12,8 @@
#include "third_party/blink/renderer/modules/webgpu/gpu_device.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_pipeline_layout.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_render_pipeline_descriptor.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_vertex_buffer_descriptor.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_vertex_input_descriptor.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_vertex_buffer_layout_descriptor.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_vertex_state_descriptor.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h"
......@@ -84,21 +84,21 @@ WGPUDepthStencilStateDescriptor AsDawnType(
return dawn_desc;
}
using WGPUVertexInputInfo = std::tuple<WGPUVertexInputDescriptor,
Vector<WGPUVertexBufferDescriptor>,
using WGPUVertexStateInfo = std::tuple<WGPUVertexStateDescriptor,
Vector<WGPUVertexBufferLayoutDescriptor>,
Vector<WGPUVertexAttributeDescriptor>>;
WGPUVertexInputInfo GPUVertexInputAsWGPUInputState(
WGPUVertexStateInfo GPUVertexStateAsWGPUVertexState(
v8::Isolate* isolate,
const GPUVertexInputDescriptor* descriptor,
const GPUVertexStateDescriptor* descriptor,
ExceptionState& exception_state) {
WGPUVertexInputDescriptor dawn_desc = {};
WGPUVertexStateDescriptor dawn_desc = {};
dawn_desc.indexFormat =
AsDawnEnum<WGPUIndexFormat>(descriptor->indexFormat());
dawn_desc.bufferCount = 0;
dawn_desc.buffers = nullptr;
dawn_desc.vertexBufferCount = 0;
dawn_desc.vertexBuffers = nullptr;
Vector<WGPUVertexBufferDescriptor> dawn_vertex_buffers;
Vector<WGPUVertexBufferLayoutDescriptor> dawn_vertex_buffers;
Vector<WGPUVertexAttributeDescriptor> dawn_vertex_attributes;
if (descriptor->hasVertexBuffers()) {
......@@ -116,18 +116,18 @@ WGPUVertexInputInfo GPUVertexInputAsWGPUInputState(
v8::Local<v8::Array> vertex_buffers = vertex_buffers_value.As<v8::Array>();
// First we collect all the descriptors but we don't set
// WGPUVertexBufferDescriptor::attributes
// WGPUVertexBufferLayoutDescriptor::attributes
// TODO(cwallez@chromium.org): Should we validate the Length() first so we
// don't risk creating HUGE vectors of WGPUVertexBufferDescriptor from the
// sparse array?
// don't risk creating HUGE vectors of WGPUVertexBufferLayoutDescriptor from
// the sparse array?
for (uint32_t i = 0; i < vertex_buffers->Length(); ++i) {
// This array can be sparse. Skip empty slots.
v8::MaybeLocal<v8::Value> maybe_value = vertex_buffers->Get(context, i);
v8::Local<v8::Value> value;
if (!maybe_value.ToLocal(&value) || value.IsEmpty() ||
value->IsNullOrUndefined()) {
WGPUVertexBufferDescriptor dawn_vertex_buffer = {};
dawn_vertex_buffer.stride = 0;
WGPUVertexBufferLayoutDescriptor dawn_vertex_buffer = {};
dawn_vertex_buffer.arrayStride = 0;
dawn_vertex_buffer.stepMode = WGPUInputStepMode_Vertex;
dawn_vertex_buffer.attributeCount = 0;
dawn_vertex_buffer.attributes = nullptr;
......@@ -135,26 +135,26 @@ WGPUVertexInputInfo GPUVertexInputAsWGPUInputState(
continue;
}
GPUVertexBufferDescriptor vertex_buffer;
V8GPUVertexBufferDescriptor::ToImpl(isolate, value, &vertex_buffer,
exception_state);
GPUVertexBufferLayoutDescriptor vertex_buffer;
V8GPUVertexBufferLayoutDescriptor::ToImpl(isolate, value, &vertex_buffer,
exception_state);
if (exception_state.HadException()) {
return std::make_tuple(dawn_desc, std::move(dawn_vertex_buffers),
std::move(dawn_vertex_attributes));
}
WGPUVertexBufferDescriptor dawn_vertex_buffer = {};
dawn_vertex_buffer.stride = vertex_buffer.stride();
WGPUVertexBufferLayoutDescriptor dawn_vertex_buffer = {};
dawn_vertex_buffer.arrayStride = vertex_buffer.arrayStride();
dawn_vertex_buffer.stepMode =
AsDawnEnum<WGPUInputStepMode>(vertex_buffer.stepMode());
dawn_vertex_buffer.attributeCount =
static_cast<uint32_t>(vertex_buffer.attributeSet().size());
static_cast<uint32_t>(vertex_buffer.attributes().size());
dawn_vertex_buffer.attributes = nullptr;
dawn_vertex_buffers.push_back(dawn_vertex_buffer);
for (wtf_size_t j = 0; j < vertex_buffer.attributeSet().size(); ++j) {
for (wtf_size_t j = 0; j < vertex_buffer.attributes().size(); ++j) {
const GPUVertexAttributeDescriptor* attribute =
vertex_buffer.attributeSet()[j];
vertex_buffer.attributes()[j];
WGPUVertexAttributeDescriptor dawn_vertex_attribute = {};
dawn_vertex_attribute.shaderLocation = attribute->shaderLocation();
dawn_vertex_attribute.offset = attribute->offset();
......@@ -164,10 +164,11 @@ WGPUVertexInputInfo GPUVertexInputAsWGPUInputState(
}
}
// Set up pointers in DawnVertexBufferDescriptor::attributes only after we
// stopped appending to the vector so the pointers aren't invalidated.
// Set up pointers in DawnVertexBufferLayoutDescriptor::attributes only
// after we stopped appending to the vector so the pointers aren't
// invalidated.
uint32_t attributeIndex = 0;
for (WGPUVertexBufferDescriptor& buffer : dawn_vertex_buffers) {
for (WGPUVertexBufferLayoutDescriptor& buffer : dawn_vertex_buffers) {
if (buffer.attributeCount == 0) {
continue;
}
......@@ -176,8 +177,9 @@ WGPUVertexInputInfo GPUVertexInputAsWGPUInputState(
}
}
dawn_desc.bufferCount = static_cast<uint32_t>(dawn_vertex_buffers.size());
dawn_desc.buffers = dawn_vertex_buffers.data();
dawn_desc.vertexBufferCount =
static_cast<uint32_t>(dawn_vertex_buffers.size());
dawn_desc.vertexBuffers = dawn_vertex_buffers.data();
return std::make_tuple(dawn_desc, std::move(dawn_vertex_buffers),
std::move(dawn_vertex_attributes));
......@@ -226,13 +228,12 @@ GPURenderPipeline* GPURenderPipeline::Create(
dawn_desc.fragmentStage = nullptr;
}
// TODO(crbug.com/dawn/131): Update Dawn to match WebGPU vertex input
v8::Isolate* isolate = script_state->GetIsolate();
ExceptionState exception_state(isolate, ExceptionState::kConstructionContext,
"GPUVertexInputDescriptor");
WGPUVertexInputInfo vertex_input_info = GPUVertexInputAsWGPUInputState(
isolate, webgpu_desc->vertexInput(), exception_state);
dawn_desc.vertexInput = &std::get<0>(vertex_input_info);
"GPUVertexStateDescriptor");
WGPUVertexStateInfo vertex_state_info = GPUVertexStateAsWGPUVertexState(
isolate, webgpu_desc->vertexState(), exception_state);
dawn_desc.vertexState = &std::get<0>(vertex_state_info);
if (exception_state.HadException()) {
return nullptr;
......
......@@ -12,7 +12,7 @@ dictionary GPURenderPipelineDescriptor : GPUPipelineDescriptorBase {
GPURasterizationStateDescriptor rasterizationState = {};
required sequence<GPUColorStateDescriptor> colorStates;
GPUDepthStencilStateDescriptor depthStencilState;
GPUVertexInputDescriptor vertexInput = {};
GPUVertexStateDescriptor vertexState = {};
unsigned long sampleCount = 1;
......
......@@ -4,10 +4,10 @@
// https://gpuweb.github.io/gpuweb/
dictionary GPUVertexBufferDescriptor {
required GPUBufferSize stride;
dictionary GPUVertexBufferLayoutDescriptor {
required GPUBufferSize arrayStride;
GPUInputStepMode stepMode = "vertex";
required sequence<GPUVertexAttributeDescriptor> attributeSet;
required sequence<GPUVertexAttributeDescriptor> attributes;
};
enum GPUInputStepMode {
......
......@@ -4,10 +4,10 @@
// https://gpuweb.github.io/gpuweb/
dictionary GPUVertexInputDescriptor {
dictionary GPUVertexStateDescriptor {
GPUIndexFormat indexFormat = "uint32";
// TODO(crbug.com/951629): Make this a sequence of nullables.
object vertexBuffers; // We validate this is an array of nullable GPUVertexBufferDescriptor
object vertexBuffers; // We validate this is an array of nullable GPUVertexBufferLayoutDescriptor
};
enum GPUIndexFormat {
......
......@@ -39,6 +39,17 @@
width: 100%;
height: 15em;
}
/* Test Name column */
#results > tbody > tr > td:nth-child(2) {
word-break: break-word;
}
/* Message column */
#results > tbody > tr > td:nth-child(3) {
white-space: pre-wrap;
word-break: break-word;
}
</style>
<textarea id=results></textarea>
......@@ -74,4 +85,4 @@
<meta name=variant content='?q=cts:validation/setStencilReference:'>
<meta name=variant content='?q=cts:validation/setVertexBuffer:'>
<meta name=variant content='?q=cts:validation/setViewport:'>
<meta name=variant content='?q=cts:validation/vertex_input:'>
<meta name=variant content='?q=cts:validation/vertex_state:'>
......@@ -27,11 +27,7 @@ export class Fixture {
async init() {}
debug(msg) {
this.rec.debug(msg);
}
log(msg) {
this.rec.log(msg);
this.rec.debug(new Error(msg));
}
skip(msg) {
......@@ -47,11 +43,11 @@ export class Fixture {
}
warn(msg) {
this.rec.warn(msg);
this.rec.warn(new Error(msg));
}
fail(msg) {
this.rec.fail(msg);
this.rec.fail(new Error(msg));
}
async immediateAsyncExpectation(fn) {
......@@ -62,35 +58,40 @@ export class Fixture {
}
eventualAsyncExpectation(fn) {
const promise = fn();
const promise = fn(new Error());
this.eventualExpectations.push(promise);
return promise;
}
expectErrorValue(expectedName, ex, m) {
expectErrorValue(expectedName, ex, niceStack) {
if (!(ex instanceof Error)) {
this.fail('THREW non-error value, of type ' + typeof ex);
niceStack.message = 'THREW non-error value, of type ' + typeof ex + niceStack.message;
this.rec.fail(niceStack);
return;
}
const actualName = ex.name;
if (actualName !== expectedName) {
this.fail(`THREW ${actualName}, instead of ${expectedName}${m}`);
niceStack.message = `THREW ${actualName}, instead of ${expectedName}` + niceStack.message;
this.rec.fail(niceStack);
} else {
this.debug(`OK: threw ${actualName}${m}`);
niceStack.message = 'OK: threw ' + actualName + niceStack.message;
this.rec.debug(niceStack);
}
}
shouldReject(expectedName, p, msg) {
this.eventualAsyncExpectation(async () => {
this.eventualAsyncExpectation(async niceStack => {
const m = msg ? ': ' + msg : '';
try {
await p;
this.fail('DID NOT THROW' + m);
niceStack.message = 'DID NOT THROW' + m;
this.rec.fail(niceStack);
} catch (ex) {
this.expectErrorValue(expectedName, ex, m);
niceStack.message = m;
this.expectErrorValue(expectedName, ex, niceStack);
}
});
}
......@@ -100,18 +101,18 @@ export class Fixture {
try {
fn();
this.fail('DID NOT THROW' + m);
this.rec.fail(new Error('DID NOT THROW' + m));
} catch (ex) {
this.expectErrorValue(expectedName, ex, m);
this.expectErrorValue(expectedName, ex, new Error(m));
}
}
expect(cond, msg) {
if (cond) {
const m = msg ? ': ' + msg : '';
this.debug('expect OK' + m);
this.rec.debug(new Error('expect OK' + m));
} else {
this.rec.fail(msg);
this.rec.fail(new Error(msg));
}
return cond;
......
......@@ -9,6 +9,34 @@ import { makeQueryString } from './url_query.js';
import { extractPublicParams } from './url_query.js';
import { getStackTrace, now } from './util/index.js';
import { version } from './version.js';
class LogMessageWithStack extends Error {
constructor(name, ex) {
super(ex.message);
this.name = name;
this.stack = ex.stack;
}
toJSON() {
let m = this.name;
if (this.message) {
m += ': ' + this.message;
}
m += '\n' + getStackTrace(this);
return m;
}
}
class LogMessageWithoutStack extends LogMessageWithStack {
toJSON() {
return this.message;
}
}
export class Logger {
constructor() {
_defineProperty(this, "results", []);
......@@ -94,46 +122,27 @@ export class TestCaseRecorder {
this.debugging = false;
}
debug(msg) {
debug(ex) {
if (!this.debugging) {
return;
}
this.log('DEBUG: ' + msg);
this.logs.push(new LogMessageWithoutStack('DEBUG', ex));
}
log(msg) {
this.logs.push(msg);
}
warn(msg) {
warn(ex) {
this.setState(PassState.warn);
let m = 'WARN';
if (msg) {
m += ': ' + msg;
}
m += ' ' + getStackTrace(new Error());
this.log(m);
this.logs.push(new LogMessageWithStack('WARN', ex));
}
fail(msg) {
fail(ex) {
this.setState(PassState.fail);
let m = 'FAIL';
if (msg) {
m += ': ' + msg;
}
m += '\n' + getStackTrace(new Error());
this.log(m);
this.logs.push(new LogMessageWithStack('FAIL', ex));
}
skipped(ex) {
this.setState(PassState.skip);
const m = 'SKIPPED: ' + getStackTrace(ex);
this.log(m);
this.logs.push(new LogMessageWithStack('SKIP', ex));
}
threw(ex) {
......@@ -143,7 +152,7 @@ export class TestCaseRecorder {
}
this.setState(PassState.fail);
this.log('EXCEPTION: ' + ex.name + ':\n' + ex.message + '\n' + getStackTrace(ex));
this.logs.push(new LogMessageWithStack('EXCEPTION', ex));
}
setState(state) {
......
// AUTO-GENERATED - DO NOT EDIT. See tools/gen_version.
export const version = '3dc37c83a70667e9a92df773f34d154ec600d203';
export const version = 'e114192747a54f34157eb65754e037701fbdf98b';
......@@ -41,7 +41,7 @@ import { TestWorker } from './helper/test_worker.js';
this.step(() => {
// Unfortunately, it seems not possible to surface any logs for warn/skip.
if (r.status === 'fail') {
throw (r.logs || []).join('\n');
throw (r.logs || []).map(s => s.toJSON()).join('\n\n');
}
});
this.done();
......
......@@ -73,7 +73,7 @@ g.test('fullscreen quad', async t => {
alphaBlend: {},
colorBlend: {}
}],
vertexInput: {
vertexState: {
indexFormat: 'uint16',
vertexBuffers: []
}
......
......@@ -112,41 +112,61 @@ export class GPUTest extends Fixture {
const c = this.device.createCommandEncoder();
c.copyBufferToBuffer(src, 0, dst, 0, size);
this.queue.submit([c.finish()]);
this.eventualAsyncExpectation(async () => {
this.eventualAsyncExpectation(async niceStack => {
const actual = new Uint8Array((await dst.mapReadAsync()));
this.expectBuffer(actual, exp);
const check = this.checkBuffer(actual, exp);
if (check !== undefined) {
niceStack.message = check;
this.rec.fail(niceStack);
}
dst.destroy();
});
}
expectBuffer(actual, exp) {
const check = this.checkBuffer(actual, exp);
if (check !== undefined) {
this.rec.fail(new Error(check));
}
}
checkBuffer(actual, exp) {
const size = exp.byteLength;
if (actual.byteLength !== size) {
this.rec.fail('size mismatch');
return;
return 'size mismatch';
}
const lines = [];
let failedPixels = 0;
for (let i = 0; i < size; ++i) {
if (actual[i] !== exp[i]) {
if (failedPixels > 4) {
this.rec.fail('... and more');
lines.push('... and more');
break;
}
failedPixels++;
this.rec.fail(`at [${i}], expected ${exp[i]}, got ${actual[i]}`);
lines.push(`at [${i}], expected ${exp[i]}, got ${actual[i]}`);
}
}
if (size <= 256 && failedPixels > 0) {
const expHex = Array.from(exp).map(x => x.toString(16).padStart(2, '0')).join('');
const actHex = Array.from(actual).map(x => x.toString(16).padStart(2, '0')).join('');
this.rec.log('EXPECT: ' + expHex);
this.rec.log('ACTUAL: ' + actHex);
lines.push('EXPECT: ' + expHex);
lines.push('ACTUAL: ' + actHex);
}
if (failedPixels) {
return lines.join('\n');
}
return undefined;
}
}
......
......@@ -130,7 +130,7 @@ export const listing = [
"description": "setViewport validation tests."
},
{
"path": "validation/vertex_input",
"description": "vertexInput validation tests."
"path": "validation/vertex_state",
"description": "vertexState validation tests."
}
];
......@@ -29,11 +29,12 @@ class F extends ValidationTest {
colorStates: [{
format: 'rgba8unorm'
}],
vertexInput: {
vertexState: {
vertexBuffers: [{
stride: 3 * 4,
attributeSet: range(bufferCount, i => ({
arrayStride: 3 * 4,
attributes: range(bufferCount, i => ({
format: 'float3',
offset: 0,
shaderLocation: i
}))
}]
......
......@@ -25,13 +25,15 @@ export class ValidationTest extends GPUTest {
this.device.pushErrorScope('validation');
fn();
const promise = this.device.popErrorScope();
this.eventualAsyncExpectation(async () => {
this.eventualAsyncExpectation(async niceStack => {
const gpuValidationError = await promise;
if (!gpuValidationError) {
this.fail('Validation error was expected.');
niceStack.message = 'Validation error was expected.';
this.rec.fail(niceStack);
} else if (gpuValidationError instanceof GPUValidationError) {
this.debug(`Captured validation error - ${gpuValidationError.message}`);
niceStack.message = `Captured validation error - ${gpuValidationError.message}`;
this.rec.debug(niceStack);
}
});
}
......
......@@ -3,13 +3,13 @@
**/
export const description = `
vertexInput validation tests.
vertexState validation tests.
`;
import { TestGroup } from '../../../framework/index.js';
import { ValidationTest } from './validation_test.js';
const MAX_VERTEX_ATTRIBUTES = 16;
const MAX_VERTEX_BUFFER_END = 2048;
const MAX_VERTEX_BUFFER_STRIDE = 2048;
const MAX_VERTEX_BUFFER_ARRAY_STRIDE = 2048;
const MAX_VERTEX_BUFFERS = 16;
const VERTEX_SHADER_CODE_WITH_NO_INPUT = `
#version 450
......@@ -27,7 +27,7 @@ class F extends ValidationTest {
await Promise.all([super.init(), this.initGLSL()]);
}
getDescriptor(vertexInput, vertexShaderCode) {
getDescriptor(vertexState, vertexShaderCode) {
const descriptor = {
vertexStage: this.getVertexStage(vertexShaderCode),
fragmentStage: this.getFragmentStage(),
......@@ -36,7 +36,7 @@ class F extends ValidationTest {
colorStates: [{
format: 'rgba8unorm'
}],
vertexInput
vertexState
};
return descriptor;
}
......@@ -72,73 +72,78 @@ class F extends ValidationTest {
export const g = new TestGroup(F);
g.test('an empty vertex input is valid', t => {
const vertexInput = {};
const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
const vertexState = {};
const descriptor = t.getDescriptor(vertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.device.createRenderPipeline(descriptor);
});
g.test('a null buffer is valid', t => {
{
// One null buffer is OK
const vertexInput = {
const vertexState = {
vertexBuffers: [{
stride: 0,
attributeSet: []
arrayStride: 0,
attributes: []
}]
};
const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
const descriptor = t.getDescriptor(vertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.device.createRenderPipeline(descriptor);
}
{
// One null buffer followed by a buffer is OK
const vertexInput = {
const vertexState = {
vertexBuffers: [{
stride: 0,
attributeSet: []
arrayStride: 0,
attributes: []
}, {
stride: 0,
attributeSet: [{
shaderLocation: 0,
format: 'float'
arrayStride: 0,
attributes: [{
format: 'float',
offset: 0,
shaderLocation: 0
}]
}]
};
const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
const descriptor = t.getDescriptor(vertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.device.createRenderPipeline(descriptor);
}
{
// One null buffer sitting between buffers is OK
const vertexInput = {
const vertexState = {
vertexBuffers: [{
stride: 0,
attributeSet: [{
shaderLocation: 0,
format: 'float'
arrayStride: 0,
attributes: [{
format: 'float',
offset: 0,
shaderLocation: 0
}]
}, {
stride: 0,
attributeSet: []
arrayStride: 0,
attributes: []
}, {
stride: 0,
attributeSet: [{
shaderLocation: 1,
format: 'float'
arrayStride: 0,
attributes: [{
format: 'float',
offset: 0,
shaderLocation: 1
}]
}]
};
const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
const descriptor = t.getDescriptor(vertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.device.createRenderPipeline(descriptor);
}
});
g.test('pipeline vertex buffers are backed by attributes in vertex input', async t => {
const vertexInput = {
const vertexState = {
vertexBuffers: [{
stride: 2 * Float32Array.BYTES_PER_ELEMENT,
attributeSet: [{
shaderLocation: 0,
format: 'float'
arrayStride: 2 * Float32Array.BYTES_PER_ELEMENT,
attributes: [{
format: 'float',
offset: 0,
shaderLocation: 0
}, {
shaderLocation: 1,
format: 'float'
format: 'float',
offset: 0,
shaderLocation: 1
}]
}]
};
......@@ -152,11 +157,11 @@ g.test('pipeline vertex buffers are backed by attributes in vertex input', async
gl_Position = vec4(0.0);
}
`;
const descriptor = t.getDescriptor(vertexInput, code);
const descriptor = t.getDescriptor(vertexState, code);
t.device.createRenderPipeline(descriptor);
}
{
// Check it is valid for the pipeline to use a subset of the VertexInput
// Check it is valid for the pipeline to use a subset of the VertexState
const code = `
#version 450
layout(location = 0) in vec4 a;
......@@ -164,7 +169,7 @@ g.test('pipeline vertex buffers are backed by attributes in vertex input', async
gl_Position = vec4(0.0);
}
`;
const descriptor = t.getDescriptor(vertexInput, code);
const descriptor = t.getDescriptor(vertexState, code);
t.device.createRenderPipeline(descriptor);
}
{
......@@ -176,103 +181,106 @@ g.test('pipeline vertex buffers are backed by attributes in vertex input', async
gl_Position = vec4(0.0);
}
`;
const descriptor = t.getDescriptor(vertexInput, code);
const descriptor = t.getDescriptor(vertexState, code);
t.expectValidationError(() => {
t.device.createRenderPipeline(descriptor);
});
}
});
g.test('a stride of 0 is valid', t => {
const vertexInput = {
g.test('an arrayStride of 0 is valid', t => {
const vertexState = {
vertexBuffers: [{
stride: 0,
attributeSet: [{
shaderLocation: 0,
format: 'float'
arrayStride: 0,
attributes: [{
format: 'float',
offset: 0,
shaderLocation: 0
}]
}]
};
{
// Works ok without attributes
const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
const descriptor = t.getDescriptor(vertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.device.createRenderPipeline(descriptor);
}
{
// Works ok with attributes at a large-ish offset
vertexInput.vertexBuffers[0].attributeSet[0].offset = 128;
const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
vertexState.vertexBuffers[0].attributes[0].offset = 128;
const descriptor = t.getDescriptor(vertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.device.createRenderPipeline(descriptor);
}
});
g.test('offset should be within vertex buffer stride if stride is not zero', async t => {
const vertexInput = {
g.test('offset should be within vertex buffer arrayStride if arrayStride is not zero', async t => {
const vertexState = {
vertexBuffers: [{
stride: 2 * Float32Array.BYTES_PER_ELEMENT,
attributeSet: [{
shaderLocation: 0,
format: 'float'
arrayStride: 2 * Float32Array.BYTES_PER_ELEMENT,
attributes: [{
format: 'float',
offset: 0,
shaderLocation: 0
}, {
format: 'float',
offset: Float32Array.BYTES_PER_ELEMENT,
shaderLocation: 1,
format: 'float'
shaderLocation: 1
}]
}]
};
{
// Control case, setting correct stride and offset
const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
// Control case, setting correct arrayStride and offset
const descriptor = t.getDescriptor(vertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.device.createRenderPipeline(descriptor);
}
{
// Test vertex attribute offset exceed vertex buffer stride range
const badVertexInput = clone(vertexInput);
badVertexInput.vertexBuffers[0].attributeSet[1].format = 'float2';
const descriptor = t.getDescriptor(badVertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
// Test vertex attribute offset exceed vertex buffer arrayStride range
const badVertexState = clone(vertexState);
badVertexState.vertexBuffers[0].attributes[1].format = 'float2';
const descriptor = t.getDescriptor(badVertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.expectValidationError(() => {
t.device.createRenderPipeline(descriptor);
});
}
{
// Test vertex attribute offset exceed vertex buffer stride range
const badVertexInput = clone(vertexInput);
badVertexInput.vertexBuffers[0].stride = Float32Array.BYTES_PER_ELEMENT;
const descriptor = t.getDescriptor(badVertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
// Test vertex attribute offset exceed vertex buffer arrayStride range
const badVertexState = clone(vertexState);
badVertexState.vertexBuffers[0].arrayStride = Float32Array.BYTES_PER_ELEMENT;
const descriptor = t.getDescriptor(badVertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.expectValidationError(() => {
t.device.createRenderPipeline(descriptor);
});
}
{
// It's OK if stride is zero
const goodVertexInput = clone(vertexInput);
goodVertexInput.vertexBuffers[0].stride = 0;
const descriptor = t.getDescriptor(goodVertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
// It's OK if arrayStride is zero
const goodVertexState = clone(vertexState);
goodVertexState.vertexBuffers[0].arrayStride = 0;
const descriptor = t.getDescriptor(goodVertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.device.createRenderPipeline(descriptor);
}
});
g.test('check two attributes overlapping', async t => {
const vertexInput = {
const vertexState = {
vertexBuffers: [{
stride: 2 * Float32Array.BYTES_PER_ELEMENT,
attributeSet: [{
shaderLocation: 0,
format: 'float'
arrayStride: 2 * Float32Array.BYTES_PER_ELEMENT,
attributes: [{
format: 'float',
offset: 0,
shaderLocation: 0
}, {
format: 'float',
offset: Float32Array.BYTES_PER_ELEMENT,
shaderLocation: 1,
format: 'float'
shaderLocation: 1
}]
}]
};
{
// Control case, setting correct stride and offset
const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
// Control case, setting correct arrayStride and offset
const descriptor = t.getDescriptor(vertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.device.createRenderPipeline(descriptor);
}
{
// Test two attributes overlapping
const badVertexInput = clone(vertexInput);
badVertexInput.vertexBuffers[0].attributeSet[0].format = 'int2';
const descriptor = t.getDescriptor(badVertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
const badVertexState = clone(vertexState);
badVertexState.vertexBuffers[0].attributes[0].format = 'int2';
const descriptor = t.getDescriptor(badVertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.expectValidationError(() => {
t.device.createRenderPipeline(descriptor);
});
......@@ -283,34 +291,36 @@ g.test('check out of bounds condition on total number of vertex buffers', async
for (let i = 0; i < MAX_VERTEX_BUFFERS; i++) {
vertexBuffers.push({
stride: 0,
attributeSet: [{
shaderLocation: i,
format: 'float'
arrayStride: 0,
attributes: [{
format: 'float',
offset: 0,
shaderLocation: i
}]
});
}
{
// Control case, setting max vertex buffer number
const vertexInput = {
const vertexState = {
vertexBuffers
};
const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
const descriptor = t.getDescriptor(vertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.device.createRenderPipeline(descriptor);
}
{
// Test vertex buffer number exceed the limit
const vertexInput = {
const vertexState = {
vertexBuffers: [...vertexBuffers, {
stride: 0,
attributeSet: [{
shaderLocation: MAX_VERTEX_BUFFERS,
format: 'float'
arrayStride: 0,
attributes: [{
format: 'float',
offset: 0,
shaderLocation: MAX_VERTEX_BUFFERS
}]
}]
};
const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
const descriptor = t.getDescriptor(vertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.expectValidationError(() => {
t.device.createRenderPipeline(descriptor);
});
......@@ -321,34 +331,36 @@ g.test('check out of bounds on number of vertex attributes on a single vertex bu
for (let i = 0; i < MAX_VERTEX_ATTRIBUTES; i++) {
vertexAttributes.push({
shaderLocation: i,
format: 'float'
format: 'float',
offset: 0,
shaderLocation: i
});
}
{
// Control case, setting max vertex buffer number
const vertexInput = {
const vertexState = {
vertexBuffers: [{
stride: 0,
attributeSet: vertexAttributes
arrayStride: 0,
attributes: vertexAttributes
}]
};
const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
const descriptor = t.getDescriptor(vertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.device.createRenderPipeline(descriptor);
}
{
// Test vertex attribute number exceed the limit
const vertexInput = {
const vertexState = {
vertexBuffers: [{
stride: 0,
attributeSet: [...vertexAttributes, {
shaderLocation: MAX_VERTEX_ATTRIBUTES,
format: 'float'
arrayStride: 0,
attributes: [...vertexAttributes, {
format: 'float',
offset: 0,
shaderLocation: MAX_VERTEX_ATTRIBUTES
}]
}]
};
const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
const descriptor = t.getDescriptor(vertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.expectValidationError(() => {
t.device.createRenderPipeline(descriptor);
});
......@@ -359,104 +371,109 @@ g.test('check out of bounds on number of vertex attributes across vertex buffers
for (let i = 0; i < MAX_VERTEX_ATTRIBUTES; i++) {
vertexBuffers.push({
stride: 0,
attributeSet: [{
shaderLocation: i,
format: 'float'
arrayStride: 0,
attributes: [{
format: 'float',
offset: 0,
shaderLocation: i
}]
});
}
{
// Control case, setting max vertex buffer number
const vertexInput = {
const vertexState = {
vertexBuffers
};
const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
const descriptor = t.getDescriptor(vertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.device.createRenderPipeline(descriptor);
}
{
// Test vertex attribute number exceed the limit
vertexBuffers[MAX_VERTEX_ATTRIBUTES - 1].attributeSet.push({
shaderLocation: MAX_VERTEX_ATTRIBUTES,
format: 'float'
vertexBuffers[MAX_VERTEX_ATTRIBUTES - 1].attributes.push({
format: 'float',
offset: 0,
shaderLocation: MAX_VERTEX_ATTRIBUTES
});
const vertexInput = {
const vertexState = {
vertexBuffers
};
const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
const descriptor = t.getDescriptor(vertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.expectValidationError(() => {
t.device.createRenderPipeline(descriptor);
});
}
});
g.test('check out of bounds condition on input strides', async t => {
const vertexInput = {
const vertexState = {
vertexBuffers: [{
stride: MAX_VERTEX_BUFFER_STRIDE,
attributeSet: []
arrayStride: MAX_VERTEX_BUFFER_ARRAY_STRIDE,
attributes: []
}]
};
{
// Control case, setting max input stride
const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
// Control case, setting max input arrayStride
const descriptor = t.getDescriptor(vertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.device.createRenderPipeline(descriptor);
}
{
// Test input stride OOB
vertexInput.vertexBuffers[0].stride = MAX_VERTEX_BUFFER_STRIDE + 4;
const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
// Test input arrayStride OOB
vertexState.vertexBuffers[0].arrayStride = MAX_VERTEX_BUFFER_ARRAY_STRIDE + 4;
const descriptor = t.getDescriptor(vertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.expectValidationError(() => {
t.device.createRenderPipeline(descriptor);
});
}
});
g.test('check multiple of 4 bytes constraint on input stride', async t => {
const vertexInput = {
g.test('check multiple of 4 bytes constraint on input arrayStride', async t => {
const vertexState = {
vertexBuffers: [{
stride: 4,
attributeSet: [{
shaderLocation: 0,
format: 'uchar2'
arrayStride: 4,
attributes: [{
format: 'uchar2',
offset: 0,
shaderLocation: 0
}]
}]
};
{
// Control case, setting input stride 4 bytes
const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
// Control case, setting input arrayStride 4 bytes
const descriptor = t.getDescriptor(vertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.device.createRenderPipeline(descriptor);
}
{
// Test input stride not multiple of 4 bytes
vertexInput.vertexBuffers[0].stride = 2;
const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
// Test input arrayStride not multiple of 4 bytes
vertexState.vertexBuffers[0].arrayStride = 2;
const descriptor = t.getDescriptor(vertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.expectValidationError(() => {
t.device.createRenderPipeline(descriptor);
});
}
});
g.test('identical duplicate attributes are invalid', async t => {
const vertexInput = {
const vertexState = {
vertexBuffers: [{
stride: 0,
attributeSet: [{
shaderLocation: 0,
format: 'float'
arrayStride: 0,
attributes: [{
format: 'float',
offset: 0,
shaderLocation: 0
}]
}]
};
{
// Control case, setting attribute 0
const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
const descriptor = t.getDescriptor(vertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.device.createRenderPipeline(descriptor);
}
{
// Oh no, attribute 0 is set twice
vertexInput.vertexBuffers[0].attributeSet.push({
shaderLocation: 0,
format: 'float'
vertexState.vertexBuffers[0].attributes.push({
format: 'float',
offset: 0,
shaderLocation: 0
});
const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
const descriptor = t.getDescriptor(vertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.expectValidationError(() => {
t.device.createRenderPipeline(descriptor);
});
......@@ -464,160 +481,164 @@ g.test('identical duplicate attributes are invalid', async t => {
});
g.test('we cannot set same shader location', async t => {
{
const vertexInput = {
const vertexState = {
vertexBuffers: [{
stride: 0,
attributeSet: [{
shaderLocation: 0,
format: 'float'
arrayStride: 0,
attributes: [{
format: 'float',
offset: 0,
shaderLocation: 0
}, {
format: 'float',
offset: Float32Array.BYTES_PER_ELEMENT,
shaderLocation: 1,
format: 'float'
shaderLocation: 1
}]
}]
};
{
// Control case, setting different shader locations in two attributes
const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
const descriptor = t.getDescriptor(vertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.device.createRenderPipeline(descriptor);
}
{
// Test same shader location in two attributes in the same buffer
vertexInput.vertexBuffers[0].attributeSet[1].shaderLocation = 0;
const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
vertexState.vertexBuffers[0].attributes[1].shaderLocation = 0;
const descriptor = t.getDescriptor(vertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.expectValidationError(() => {
t.device.createRenderPipeline(descriptor);
});
}
}
{
const vertexInput = {
const vertexState = {
vertexBuffers: [{
stride: 0,
attributeSet: [{
shaderLocation: 0,
format: 'float'
arrayStride: 0,
attributes: [{
format: 'float',
offset: 0,
shaderLocation: 0
}]
}, {
stride: 0,
attributeSet: [{
shaderLocation: 0,
format: 'float'
arrayStride: 0,
attributes: [{
format: 'float',
offset: 0,
shaderLocation: 0
}]
}]
}; // Test same shader location in two attributes in different buffers
const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
const descriptor = t.getDescriptor(vertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.expectValidationError(() => {
t.device.createRenderPipeline(descriptor);
});
}
});
g.test('check out of bounds condition on attribute shader location', async t => {
const vertexInput = {
const vertexState = {
vertexBuffers: [{
stride: 0,
attributeSet: [{
shaderLocation: MAX_VERTEX_ATTRIBUTES - 1,
format: 'float'
arrayStride: 0,
attributes: [{
format: 'float',
offset: 0,
shaderLocation: MAX_VERTEX_ATTRIBUTES - 1
}]
}]
};
{
// Control case, setting last attribute shader location
const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
const descriptor = t.getDescriptor(vertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.device.createRenderPipeline(descriptor);
}
{
// Test attribute location OOB
vertexInput.vertexBuffers[0].attributeSet[0].shaderLocation = MAX_VERTEX_ATTRIBUTES;
const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
vertexState.vertexBuffers[0].attributes[0].shaderLocation = MAX_VERTEX_ATTRIBUTES;
const descriptor = t.getDescriptor(vertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.expectValidationError(() => {
t.device.createRenderPipeline(descriptor);
});
}
});
g.test('check attribute offset out of bounds', async t => {
const vertexInput = {
const vertexState = {
vertexBuffers: [{
stride: 0,
attributeSet: [{
arrayStride: 0,
attributes: [{
format: 'float2',
offset: MAX_VERTEX_BUFFER_END - 2 * Float32Array.BYTES_PER_ELEMENT,
shaderLocation: 0,
format: 'float2'
shaderLocation: 0
}]
}]
};
{
// Control case, setting max attribute offset to MAX_VERTEX_BUFFER_END - 8
const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
const descriptor = t.getDescriptor(vertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.device.createRenderPipeline(descriptor);
}
{
// Control case, setting attribute offset to 8
vertexInput.vertexBuffers[0].attributeSet[0].offset = 8;
const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
vertexState.vertexBuffers[0].attributes[0].offset = 8;
const descriptor = t.getDescriptor(vertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.device.createRenderPipeline(descriptor);
}
{
// Test attribute offset out of bounds
vertexInput.vertexBuffers[0].attributeSet[0].offset = MAX_VERTEX_BUFFER_END - 4;
const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
vertexState.vertexBuffers[0].attributes[0].offset = MAX_VERTEX_BUFFER_END - 4;
const descriptor = t.getDescriptor(vertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.expectValidationError(() => {
t.device.createRenderPipeline(descriptor);
});
}
});
g.test('check multiple of 4 bytes constraint on offset', async t => {
const vertexInput = {
const vertexState = {
vertexBuffers: [{
stride: 0,
attributeSet: [{
arrayStride: 0,
attributes: [{
format: 'float',
offset: Float32Array.BYTES_PER_ELEMENT,
shaderLocation: 0,
format: 'float'
shaderLocation: 0
}]
}]
};
{
// Control case, setting offset 4 bytes
const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
const descriptor = t.getDescriptor(vertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.device.createRenderPipeline(descriptor);
}
{
// Test offset of 2 bytes with uchar2 format
vertexInput.vertexBuffers[0].attributeSet[0].offset = 2;
vertexInput.vertexBuffers[0].attributeSet[0].format = 'uchar2';
const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
vertexState.vertexBuffers[0].attributes[0].offset = 2;
vertexState.vertexBuffers[0].attributes[0].format = 'uchar2';
const descriptor = t.getDescriptor(vertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.expectValidationError(() => {
t.device.createRenderPipeline(descriptor);
});
}
{
// Test offset of 2 bytes with float format
vertexInput.vertexBuffers[0].attributeSet[0].offset = 2;
vertexInput.vertexBuffers[0].attributeSet[0].format = 'float';
const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
vertexState.vertexBuffers[0].attributes[0].offset = 2;
vertexState.vertexBuffers[0].attributes[0].format = 'float';
const descriptor = t.getDescriptor(vertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.expectValidationError(() => {
t.device.createRenderPipeline(descriptor);
});
}
});
g.test('check attribute offset overflow', async t => {
const vertexInput = {
const vertexState = {
vertexBuffers: [{
stride: 0,
attributeSet: [{
arrayStride: 0,
attributes: [{
format: 'float',
offset: Number.MAX_SAFE_INTEGER,
shaderLocation: 0,
format: 'float'
shaderLocation: 0
}]
}]
};
const descriptor = t.getDescriptor(vertexInput, VERTEX_SHADER_CODE_WITH_NO_INPUT);
const descriptor = t.getDescriptor(vertexState, VERTEX_SHADER_CODE_WITH_NO_INPUT);
t.expectValidationError(() => {
t.device.createRenderPipeline(descriptor);
});
});
//# sourceMappingURL=vertex_input.spec.js.map
\ No newline at end of file
//# sourceMappingURL=vertex_state.spec.js.map
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment