navigator_gpu.js (6624B)
1 /** 2 * AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts 3 **/import { globalTestConfig } from '../framework/test_config.js'; 4 5 import { ErrorWithExtra, assert, hasFeature, objectEquals } from './util.js'; 6 7 /** 8 * Finds and returns the `navigator.gpu` object (or equivalent, for non-browser implementations). 9 * Throws an exception if not found. 10 */ 11 function defaultGPUProvider() { 12 assert( 13 typeof navigator !== 'undefined' && navigator.gpu !== undefined, 14 'No WebGPU implementation found' 15 ); 16 return navigator.gpu; 17 } 18 19 /** 20 * GPUProvider is a function that creates and returns a new GPU instance. 21 * May throw an exception if a GPU cannot be created. 22 */ 23 24 25 let gpuProvider = defaultGPUProvider; 26 27 /** 28 * Sets the function to create and return a new GPU instance. 29 */ 30 export function setGPUProvider(provider) { 31 assert(impl === undefined, 'setGPUProvider() should not be after getGPU()'); 32 gpuProvider = provider; 33 } 34 35 let impl = undefined; 36 let s_defaultLimits = undefined; 37 38 let defaultRequestAdapterOptions; 39 40 export function setDefaultRequestAdapterOptions(options) { 41 // It's okay to call this if you don't change the options 42 if (objectEquals(options, defaultRequestAdapterOptions)) { 43 return; 44 } 45 if (impl) { 46 throw new Error('must call setDefaultRequestAdapterOptions before getGPU'); 47 } 48 defaultRequestAdapterOptions = { ...options }; 49 } 50 51 export function getDefaultRequestAdapterOptions() { 52 return defaultRequestAdapterOptions; 53 } 54 55 function copyLimits(objLike) { 56 const obj = {}; 57 for (const key in objLike) { 58 obj[key] = objLike[key]; 59 } 60 return obj; 61 } 62 63 /** 64 * Finds and returns the `navigator.gpu` object (or equivalent, for non-browser implementations). 65 * Throws an exception if not found. 66 */ 67 export function getGPU(recorder) { 68 if (impl) { 69 return impl; 70 } 71 72 impl = gpuProvider(); 73 74 if (globalTestConfig.enforceDefaultLimits) { 75 76 const origRequestAdapterFn = impl.requestAdapter; 77 78 const origRequestDeviceFn = GPUAdapter.prototype.requestDevice; 79 80 Object.defineProperty(impl, 'requestAdapter', { 81 configurable: true, 82 async value(options) { 83 if (!s_defaultLimits) { 84 const tempAdapter = await origRequestAdapterFn.call(this, { 85 ...defaultRequestAdapterOptions, 86 ...options 87 }); 88 89 const tempDevice = await tempAdapter?.requestDevice(); 90 s_defaultLimits = copyLimits(tempDevice.limits); 91 tempDevice?.destroy(); 92 } 93 const adapter = await origRequestAdapterFn.call(this, { 94 ...defaultRequestAdapterOptions, 95 ...options 96 }); 97 if (adapter) { 98 const limits = Object.fromEntries( 99 Object.entries(s_defaultLimits).map(([key, v]) => [key, v]) 100 ); 101 102 Object.defineProperty(adapter, 'limits', { 103 get() { 104 return limits; 105 } 106 }); 107 } 108 return adapter; 109 } 110 }); 111 112 const enforceDefaultLimits = (adapter, desc) => { 113 if (desc?.requiredLimits) { 114 for (const [key, value] of Object.entries(desc.requiredLimits)) { 115 const limit = s_defaultLimits[key]; 116 if (limit !== undefined && value !== undefined) { 117 const [beyondLimit, condition] = key.startsWith('max') ? 118 [value > limit, 'greater'] : 119 [value < limit, 'less']; 120 if (beyondLimit) { 121 throw new DOMException( 122 `requestedLimit ${value} for ${key} is ${condition} than adapter limit ${limit}`, 123 'OperationError' 124 ); 125 } 126 } 127 } 128 } 129 }; 130 131 GPUAdapter.prototype.requestDevice = async function ( 132 133 desc) 134 { 135 // We need to enforce the default limits because even though we patched the adapter to 136 // show defaults for adapter.limits, there are tests that test we throw when we request more than the max. 137 // In other words. 138 // 139 // adapter.requestDevice({ requiredLimits: { 140 // maxXXX: adapter.limits.maxXXX + 1, // should throw 141 // }); 142 // 143 // But unless we enforce this manually, it won't actually throw if the adapter's 144 // true limits are higher than we patched above. 145 enforceDefaultLimits(this, desc); 146 return await origRequestDeviceFn.call(this, desc); 147 }; 148 } 149 150 if (globalTestConfig.blockAllFeatures) { 151 152 const origRequestAdapterFn = impl.requestAdapter; 153 154 const origRequestDeviceFn = GPUAdapter.prototype.requestDevice; 155 156 Object.defineProperty(impl, 'requestAdapter', { 157 configurable: true, 158 async value(options) { 159 const adapter = await origRequestAdapterFn.call(this, { 160 ...defaultRequestAdapterOptions, 161 ...options 162 }); 163 if (adapter) { 164 Object.defineProperty(adapter, 'features', { 165 enumerable: false, 166 value: new Set( 167 hasFeature(adapter.features, 'core-features-and-limits') ? 168 ['core-features-and-limits'] : 169 [] 170 ) 171 }); 172 } 173 return adapter; 174 } 175 }); 176 177 const enforceBlockedFeatures = (adapter, desc) => { 178 if (desc?.requiredFeatures) { 179 for (const [feature] of desc.requiredFeatures) { 180 // Note: This adapter has had its features property over-ridden and will only return 181 // have nothing or 'core-features-and-limits'. 182 183 if (!adapter.features.has(feature)) { 184 throw new TypeError(`requested feature ${feature} does not exist on adapter`); 185 } 186 } 187 } 188 }; 189 190 GPUAdapter.prototype.requestDevice = async function ( 191 192 desc) 193 { 194 // We need to enforce the feature block because even though we patched the adapter to 195 // advertise no features they still exist on the real adapter. 196 enforceBlockedFeatures(this, desc); 197 return await origRequestDeviceFn.call(this, desc); 198 }; 199 } 200 201 if (defaultRequestAdapterOptions) { 202 203 const origRequestAdapterFn = impl.requestAdapter; 204 205 206 Object.defineProperty(impl, 'requestAdapter', { 207 configurable: true, 208 async value(options) { 209 const adapter = await origRequestAdapterFn.call(this, { 210 ...defaultRequestAdapterOptions, 211 ...options 212 }); 213 if (recorder && adapter) { 214 const adapterInfo = adapter.info; 215 const infoString = `Adapter: ${adapterInfo.vendor} / ${adapterInfo.architecture} / ${adapterInfo.device}`; 216 recorder.debug(new ErrorWithExtra(infoString, () => ({ adapterInfo }))); 217 } 218 return adapter; 219 } 220 }); 221 } 222 223 return impl; 224 }