import { DEVICETYPE_WEBGPU, UNUSED_UNIFORM_NAME, PIXELFORMAT_SRGBA8, PIXELFORMAT_RGBA8, PIXELFORMAT_SBGRA8, PIXELFORMAT_BGRA8, DISPLAYFORMAT_LDR_SRGB, DISPLAYFORMAT_HDR, PIXELFORMAT_RGBA16F, BUFFERUSAGE_INDIRECT, BUFFERUSAGE_COPY_DST, BUFFERUSAGE_READ, semanticToLocation } from '../constants.js';
import { BindGroupFormat } from '../bind-group-format.js';
import { BindGroup } from '../bind-group.js';
import { GraphicsDevice } from '../graphics-device.js';
import { RenderTarget } from '../render-target.js';
import { StencilParameters } from '../stencil-parameters.js';
import { WebgpuBindGroup } from './webgpu-bind-group.js';
import { WebgpuBindGroupFormat } from './webgpu-bind-group-format.js';
import { WebgpuIndexBuffer } from './webgpu-index-buffer.js';
import { WebgpuRenderPipeline } from './webgpu-render-pipeline.js';
import { WebgpuComputePipeline } from './webgpu-compute-pipeline.js';
import { WebgpuRenderTarget } from './webgpu-render-target.js';
import { WebgpuShader } from './webgpu-shader.js';
import { WebgpuTexture } from './webgpu-texture.js';
import { WebgpuUniformBuffer } from './webgpu-uniform-buffer.js';
import { WebgpuVertexBuffer } from './webgpu-vertex-buffer.js';
import { WebgpuClearRenderer } from './webgpu-clear-renderer.js';
import { WebgpuMipmapRenderer } from './webgpu-mipmap-renderer.js';
import { WebgpuDynamicBuffers } from './webgpu-dynamic-buffers.js';
import { WebgpuGpuProfiler } from './webgpu-gpu-profiler.js';
import { WebgpuResolver } from './webgpu-resolver.js';
import { WebgpuCompute } from './webgpu-compute.js';
import { WebgpuBuffer } from './webgpu-buffer.js';
import { StorageBuffer } from '../storage-buffer.js';

const _uniqueLocations = new Map();
const _indirectEntryByteSize = 5 * 4;
class WebgpuGraphicsDevice extends GraphicsDevice {
		constructor(canvas, options = {}){
				super(canvas, options), this.renderPipeline = new WebgpuRenderPipeline(this), this.computePipeline = new WebgpuComputePipeline(this), this._indirectDrawBuffer = null, this._indirectDrawBufferCount = 0, this._indirectDrawNextIndex = 0, this.pipeline = null, this.bindGroupFormats = [], this.commandEncoder = null, this.commandBuffers = [], this.glslang = null, this.twgsl = null;
				options = this.initOptions;
				options.alpha = options.alpha ?? true;
				this.backBufferAntialias = options.antialias ?? false;
				this.isWebGPU = true;
				this._deviceType = DEVICETYPE_WEBGPU;
				this.scope.resolve(UNUSED_UNIFORM_NAME).setValue(0);
		}
		destroy() {
				this.clearRenderer.destroy();
				this.clearRenderer = null;
				this.mipmapRenderer.destroy();
				this.mipmapRenderer = null;
				this.resolver.destroy();
				this.resolver = null;
				super.destroy();
		}
		initDeviceCaps() {
				const limits = this.wgpu?.limits;
				this.limits = limits;
				this.precision = 'highp';
				this.maxPrecision = 'highp';
				this.maxSamples = 4;
				this.maxTextures = 16;
				this.maxTextureSize = limits.maxTextureDimension2D;
				this.maxCubeMapSize = limits.maxTextureDimension2D;
				this.maxVolumeSize = limits.maxTextureDimension3D;
				this.maxColorAttachments = limits.maxColorAttachments;
				this.maxPixelRatio = 1;
				this.maxAnisotropy = 16;
				this.fragmentUniformsCount = limits.maxUniformBufferBindingSize / 16;
				this.vertexUniformsCount = limits.maxUniformBufferBindingSize / 16;
				this.supportsUniformBuffers = true;
				this.supportsAreaLights = true;
				this.supportsGpuParticles = true;
				this.supportsCompute = true;
				this.textureFloatRenderable = true;
				this.textureHalfFloatRenderable = true;
				this.supportsImageBitmap = true;
				this.samples = this.backBufferAntialias ? 4 : 1;
				const wgslFeatures = window.navigator.gpu.wgslLanguageFeatures;
				this.supportsStorageTextureRead = wgslFeatures?.has('readonly_and_readwrite_storage_textures');
				this.initCapsDefines();
		}
		async initWebGpu(glslangUrl, twgslUrl) {
				if (!window.navigator.gpu) {
						throw new Error('Unable to retrieve GPU. Ensure you are using a browser that supports WebGPU rendering.');
				}
				if (glslangUrl && twgslUrl) {
						const buildUrl = (srcPath)=>{
								return new URL(srcPath, window.location.href).toString();
						};
						const results = await Promise.all([
								import(/* @vite-ignore */ /* webpackIgnore: true */ `${buildUrl(twgslUrl)}`).then((module)=>twgsl(twgslUrl.replace('.js', '.wasm'))),
								import(/* @vite-ignore */ /* webpackIgnore: true */ `${buildUrl(glslangUrl)}`).then((module)=>module.default())
						]);
						this.twgsl = results[0];
						this.glslang = results[1];
				}
				return this.createDevice();
		}
		async createDevice() {
				const adapterOptions = {
						powerPreference: this.initOptions.powerPreference !== 'default' ? this.initOptions.powerPreference : undefined,
						xrCompatible: this.initOptions.xrCompatible
				};
				this.gpuAdapter = await window.navigator.gpu.requestAdapter(adapterOptions);
				const requiredFeatures = [];
				const requireFeature = (feature)=>{
						const supported = this.gpuAdapter.features.has(feature);
						if (supported) {
								requiredFeatures.push(feature);
						}
						return supported;
				};
				this.textureFloatFilterable = requireFeature('float32-filterable');
				this.textureFloatBlendable = requireFeature('float32-blendable');
				this.extCompressedTextureS3TC = requireFeature('texture-compression-bc');
				this.extCompressedTextureS3TCSliced3D = requireFeature('texture-compression-bc-sliced-3d');
				this.extCompressedTextureETC = requireFeature('texture-compression-etc2');
				this.extCompressedTextureASTC = requireFeature('texture-compression-astc');
				this.extCompressedTextureASTCSliced3D = requireFeature('texture-compression-astc-sliced-3d');
				this.supportsTimestampQuery = requireFeature('timestamp-query');
				this.supportsDepthClip = requireFeature('depth-clip-control');
				this.supportsDepth32Stencil = requireFeature('depth32float-stencil8');
				this.supportsIndirectFirstInstance = requireFeature('indirect-first-instance');
				this.supportsShaderF16 = requireFeature('shader-f16');
				this.supportsStorageRGBA8 = requireFeature('bgra8unorm-storage');
				this.textureRG11B10Renderable = requireFeature('rg11b10ufloat-renderable');
				this.supportsClipDistances = requireFeature('clip-distances');
				const adapterLimits = this.gpuAdapter?.limits;
				const requiredLimits = {};
				if (adapterLimits) {
						for(const limitName in adapterLimits){
								if (limitName === 'minSubgroupSize' || limitName === 'maxSubgroupSize') {
										continue;
								}
								requiredLimits[limitName] = adapterLimits[limitName];
						}
				}
				const deviceDescr = {
						requiredFeatures,
						requiredLimits,
						defaultQueue: {
								label: 'Default Queue'
						}
				};
				this.wgpu = await this.gpuAdapter.requestDevice(deviceDescr);
				this.wgpu.lost?.then(this.handleDeviceLost.bind(this));
				this.initDeviceCaps();
				this.gpuContext = this.canvas.getContext('webgpu');
				let canvasToneMapping = 'standard';
				let preferredCanvasFormat = window.navigator.gpu.getPreferredCanvasFormat();
				const displayFormat = this.initOptions.displayFormat;
				this.backBufferFormat = preferredCanvasFormat === 'rgba8unorm' ? displayFormat === DISPLAYFORMAT_LDR_SRGB ? PIXELFORMAT_SRGBA8 : PIXELFORMAT_RGBA8 : displayFormat === DISPLAYFORMAT_LDR_SRGB ? PIXELFORMAT_SBGRA8 : PIXELFORMAT_BGRA8;
				this.backBufferViewFormat = displayFormat === DISPLAYFORMAT_LDR_SRGB ? `${preferredCanvasFormat}-srgb` : preferredCanvasFormat;
				if (displayFormat === DISPLAYFORMAT_HDR && this.textureFloatFilterable) {
						const hdrMediaQuery = window.matchMedia('(dynamic-range: high)');
						if (hdrMediaQuery?.matches) {
								this.backBufferFormat = PIXELFORMAT_RGBA16F;
								this.backBufferViewFormat = 'rgba16float';
								preferredCanvasFormat = 'rgba16float';
								this.isHdr = true;
								canvasToneMapping = 'extended';
						}
				}
				this.canvasConfig = {
						device: this.wgpu,
						colorSpace: 'srgb',
						alphaMode: this.initOptions.alpha ? 'premultiplied' : 'opaque',
						format: preferredCanvasFormat,
						toneMapping: {
								mode: canvasToneMapping
						},
						usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC | GPUTextureUsage.COPY_DST,
						viewFormats: displayFormat === DISPLAYFORMAT_LDR_SRGB ? [
								this.backBufferViewFormat
						] : []
				};
				this.gpuContext?.configure(this.canvasConfig);
				this.createBackbuffer();
				this.clearRenderer = new WebgpuClearRenderer(this);
				this.mipmapRenderer = new WebgpuMipmapRenderer(this);
				this.resolver = new WebgpuResolver(this);
				this.postInit();
				return this;
		}
		async handleDeviceLost(info) {
				if (info.reason !== 'destroyed') {
						super.loseContext();
						await this.createDevice();
						super.restoreContext();
				}
		}
		postInit() {
				super.postInit();
				this.initializeRenderState();
				this.setupPassEncoderDefaults();
				this.gpuProfiler = new WebgpuGpuProfiler(this);
				this.dynamicBuffers = new WebgpuDynamicBuffers(this, 100 * 1024, this.limits.minUniformBufferOffsetAlignment);
				this.emptyBindGroup = new BindGroup(this, new BindGroupFormat(this, []));
				this.emptyBindGroup.update();
		}
		createBackbuffer() {
				this.supportsStencil = this.initOptions.stencil;
				this.backBuffer = new RenderTarget({
						name: 'WebgpuFramebuffer',
						graphicsDevice: this,
						depth: this.initOptions.depth,
						stencil: this.supportsStencil,
						samples: this.samples
				});
				this.backBuffer.impl.isBackbuffer = true;
		}
		frameStart() {
				super.frameStart();
				this.gpuProfiler.frameStart();
				this.submit();
				const outColorBuffer = this.gpuContext?.getCurrentTexture?.() ?? this.externalBackbuffer?.impl.gpuTexture;
				if (this.backBufferSize.x !== outColorBuffer.width || this.backBufferSize.y !== outColorBuffer.height) {
						this.backBufferSize.set(outColorBuffer.width, outColorBuffer.height);
						this.backBuffer.destroy();
						this.backBuffer = null;
						this.createBackbuffer();
				}
				const rt = this.backBuffer;
				const wrt = rt.impl;
				wrt.setColorAttachment(0, undefined, this.backBufferViewFormat);
				this.initRenderTarget(rt);
				wrt.assignColorTexture(this, outColorBuffer);
		}
		frameEnd() {
				super.frameEnd();
				this.gpuProfiler.frameEnd();
				this.submit();
				if (!this.contextLost) {
						this.gpuProfiler.request();
				}
				this._indirectDrawNextIndex = 0;
		}
		createBufferImpl(usageFlags) {
				return new WebgpuBuffer(usageFlags);
		}
		createUniformBufferImpl(uniformBuffer) {
				return new WebgpuUniformBuffer(uniformBuffer);
		}
		createVertexBufferImpl(vertexBuffer, format, options) {
				return new WebgpuVertexBuffer(vertexBuffer, format, options);
		}
		createIndexBufferImpl(indexBuffer, options) {
				return new WebgpuIndexBuffer(indexBuffer, options);
		}
		createShaderImpl(shader) {
				return new WebgpuShader(shader);
		}
		createTextureImpl(texture) {
				return new WebgpuTexture(texture);
		}
		createRenderTargetImpl(renderTarget) {
				return new WebgpuRenderTarget(renderTarget);
		}
		createBindGroupFormatImpl(bindGroupFormat) {
				return new WebgpuBindGroupFormat(bindGroupFormat);
		}
		createBindGroupImpl(bindGroup) {
				return new WebgpuBindGroup();
		}
		createComputeImpl(compute) {
				return new WebgpuCompute(compute);
		}
		get indirectDrawBuffer() {
				this.allocateIndirectDrawBuffer();
				return this._indirectDrawBuffer;
		}
		allocateIndirectDrawBuffer() {
				if (this._indirectDrawNextIndex === 0 && this._indirectDrawBufferCount < this.maxIndirectDrawCount) {
						this._indirectDrawBuffer?.destroy();
						this._indirectDrawBuffer = null;
				}
				if (this._indirectDrawBuffer === null) {
						this._indirectDrawBuffer = new StorageBuffer(this, this.maxIndirectDrawCount * 4, BUFFERUSAGE_INDIRECT | BUFFERUSAGE_COPY_DST);
						this._indirectDrawBufferCount = this.maxIndirectDrawCount;
				}
		}
		getIndirectDrawSlot() {
				this.allocateIndirectDrawBuffer();
				const slot = this._indirectDrawNextIndex;
				this._indirectDrawNextIndex++;
				return slot;
		}
		setBindGroup(index, bindGroup, offsets) {
				if (this.passEncoder) {
						this.passEncoder.setBindGroup(index, bindGroup.impl.bindGroup, offsets ?? bindGroup.uniformBufferOffsets);
						this.bindGroupFormats[index] = bindGroup.format.impl;
				}
		}
		submitVertexBuffer(vertexBuffer, slot) {
				const format = vertexBuffer.format;
				const { interleaved, elements } = format;
				const elementCount = elements.length;
				const vbBuffer = vertexBuffer.impl.buffer;
				if (interleaved) {
						this.passEncoder.setVertexBuffer(slot, vbBuffer);
						return 1;
				}
				for(let i = 0; i < elementCount; i++){
						this.passEncoder.setVertexBuffer(slot + i, vbBuffer, elements[i].offset);
				}
				return elementCount;
		}
		validateVBLocations(vb0, vb1) {
				const validateVB = (vb)=>{
						const { elements } = vb.format;
						for(let i = 0; i < elements.length; i++){
								const name = elements[i].name;
								const location = semanticToLocation[name];
								if (_uniqueLocations.has(location)) ;
								_uniqueLocations.set(location, name);
						}
				};
				validateVB(vb0);
				validateVB(vb1);
				_uniqueLocations.clear();
		}
		draw(primitive, indexBuffer, numInstances = 1, indirectSlot, first = true, last = true) {
				if (this.shader.ready && !this.shader.failed) {
						const passEncoder = this.passEncoder;
						let pipeline = this.pipeline;
						const vb0 = this.vertexBuffers[0];
						const vb1 = this.vertexBuffers[1];
						if (first) {
								if (vb0) {
										const vbSlot = this.submitVertexBuffer(vb0, 0);
										if (vb1) {
												this.submitVertexBuffer(vb1, vbSlot);
										}
								}
								pipeline = this.renderPipeline.get(primitive, vb0?.format, vb1?.format, indexBuffer?.format, this.shader, this.renderTarget, this.bindGroupFormats, this.blendState, this.depthState, this.cullMode, this.stencilEnabled, this.stencilFront, this.stencilBack);
								if (this.pipeline !== pipeline) {
										this.pipeline = pipeline;
										passEncoder.setPipeline(pipeline);
								}
						}
						if (indexBuffer) {
								passEncoder.setIndexBuffer(indexBuffer.impl.buffer, indexBuffer.impl.format);
						}
						if (indirectSlot !== undefined) {
								const indirectOffset = indirectSlot * _indirectEntryByteSize;
								const indirectBuffer = this.indirectDrawBuffer.impl.buffer;
								if (indexBuffer) {
										passEncoder.drawIndexedIndirect(indirectBuffer, indirectOffset);
								} else {
										passEncoder.drawIndirect(indirectBuffer, indirectOffset);
								}
						} else {
								if (indexBuffer) {
										passEncoder.drawIndexed(primitive.count, numInstances, primitive.base, primitive.baseVertex ?? 0, 0);
								} else {
										passEncoder.draw(primitive.count, numInstances, primitive.base, 0);
								}
						}
				}
				if (last) {
						this.clearVertexBuffer();
						this.pipeline = null;
				}
		}
		setShader(shader, asyncCompile = false) {
				if (shader !== this.shader) {
						this.shader = shader;
						this._shaderSwitchesPerFrame++;
				}
		}
		setBlendState(blendState) {
				this.blendState.copy(blendState);
		}
		setDepthState(depthState) {
				this.depthState.copy(depthState);
		}
		setStencilState(stencilFront, stencilBack) {
				if (stencilFront || stencilBack) {
						this.stencilEnabled = true;
						this.stencilFront.copy(stencilFront ?? StencilParameters.DEFAULT);
						this.stencilBack.copy(stencilBack ?? StencilParameters.DEFAULT);
						const ref = this.stencilFront.ref;
						if (this.stencilRef !== ref) {
								this.stencilRef = ref;
								this.passEncoder.setStencilReference(ref);
						}
				} else {
						this.stencilEnabled = false;
				}
		}
		setBlendColor(r, g, b, a) {
				const c = this.blendColor;
				if (r !== c.r || g !== c.g || b !== c.b || a !== c.a) {
						c.set(r, g, b, a);
						this.passEncoder.setBlendConstant(c);
				}
		}
		setCullMode(cullMode) {
				this.cullMode = cullMode;
		}
		setAlphaToCoverage(state) {}
		initializeContextCaches() {
				super.initializeContextCaches();
		}
		setupPassEncoderDefaults() {
				this.pipeline = null;
				this.stencilRef = 0;
				this.blendColor.set(0, 0, 0, 0);
		}
		_uploadDirtyTextures() {
				this.textures.forEach((texture)=>{
						if (texture._needsUpload || texture._needsMipmaps) {
								texture.upload();
						}
				});
		}
		setupTimeStampWrites(passDesc, name) {
				if (this.gpuProfiler._enabled) {
						if (this.gpuProfiler.timestampQueriesSet) {
								const slot = this.gpuProfiler.getSlot(name);
								passDesc = passDesc ?? {};
								passDesc.timestampWrites = {
										querySet: this.gpuProfiler.timestampQueriesSet.querySet,
										beginningOfPassWriteIndex: slot * 2,
										endOfPassWriteIndex: slot * 2 + 1
								};
						}
				}
				return passDesc;
		}
		startRenderPass(renderPass) {
				this._uploadDirtyTextures();
				const rt = renderPass.renderTarget || this.backBuffer;
				this.renderTarget = rt;
				const wrt = rt.impl;
				if (rt !== this.backBuffer) {
						this.initRenderTarget(rt);
				}
				wrt.setupForRenderPass(renderPass, rt);
				const renderPassDesc = wrt.renderPassDescriptor;
				this.setupTimeStampWrites(renderPassDesc, renderPass.name);
				const commandEncoder = this.getCommandEncoder();
				this.passEncoder = commandEncoder.beginRenderPass(renderPassDesc);
				this.passEncoder.label = `${renderPass.name}-PassEncoder RT:${rt.name}`;
				this.setupPassEncoderDefaults();
				const { width, height } = rt;
				this.setViewport(0, 0, width, height);
				this.setScissor(0, 0, width, height);
				this.insideRenderPass = true;
		}
		endRenderPass(renderPass) {
				this.passEncoder.end();
				this.passEncoder = null;
				this.insideRenderPass = false;
				this.bindGroupFormats.length = 0;
				const target = this.renderTarget;
				if (target) {
						if (target.depthBuffer && renderPass.depthStencilOps.resolveDepth) {
								if (renderPass.samples > 1 && target.autoResolve) {
										const depthAttachment = target.impl.depthAttachment;
										const destTexture = target.depthBuffer.impl.gpuTexture;
										if (depthAttachment && destTexture) {
												this.resolver.resolveDepth(this.commandEncoder, depthAttachment.multisampledDepthBuffer, destTexture);
										}
								}
						}
				}
				for(let i = 0; i < renderPass.colorArrayOps.length; i++){
						const colorOps = renderPass.colorArrayOps[i];
						if (colorOps.genMipmaps) {
								this.mipmapRenderer.generate(renderPass.renderTarget._colorBuffers[i].impl);
						}
				}
		}
		startComputePass(name) {
				this.pipeline = null;
				const computePassDesc = this.setupTimeStampWrites(undefined, name);
				const commandEncoder = this.getCommandEncoder();
				this.passEncoder = commandEncoder.beginComputePass(computePassDesc);
				this.insideRenderPass = true;
		}
		endComputePass() {
				this.passEncoder.end();
				this.passEncoder = null;
				this.insideRenderPass = false;
				this.bindGroupFormats.length = 0;
		}
		computeDispatch(computes, name = 'Unnamed') {
				this.startComputePass(name);
				for(let i = 0; i < computes.length; i++){
						const compute = computes[i];
						compute.applyParameters();
						compute.impl.updateBindGroup();
				}
				for(let i = 0; i < computes.length; i++){
						const compute = computes[i];
						compute.impl.dispatch(compute.countX, compute.countY, compute.countZ);
				}
				this.endComputePass();
		}
		getCommandEncoder() {
				let commandEncoder = this.commandEncoder;
				if (!commandEncoder) {
						commandEncoder = this.wgpu.createCommandEncoder();
						this.commandEncoder = commandEncoder;
				}
				return commandEncoder;
		}
		endCommandEncoder() {
				const { commandEncoder } = this;
				if (commandEncoder) {
						const cb = commandEncoder.finish();
						this.addCommandBuffer(cb);
						this.commandEncoder = null;
				}
		}
		addCommandBuffer(commandBuffer, front = false) {
				if (front) {
						this.commandBuffers.unshift(commandBuffer);
				} else {
						this.commandBuffers.push(commandBuffer);
				}
		}
		submit() {
				this.endCommandEncoder();
				if (this.commandBuffers.length > 0) {
						this.dynamicBuffers.submit();
						this.wgpu.queue.submit(this.commandBuffers);
						this.commandBuffers.length = 0;
						this.dynamicBuffers.onCommandBuffersSubmitted();
				}
		}
		clear(options) {
				if (options.flags) {
						this.clearRenderer.clear(this, this.renderTarget, options, this.defaultClearOptions);
				}
		}
		setViewport(x, y, w, h) {
				if (this.passEncoder) {
						if (!this.renderTarget.flipY) {
								y = this.renderTarget.height - y - h;
						}
						this.vx = x;
						this.vy = y;
						this.vw = w;
						this.vh = h;
						this.passEncoder.setViewport(x, y, w, h, 0, 1);
				}
		}
		setScissor(x, y, w, h) {
				if (this.passEncoder) {
						if (!this.renderTarget.flipY) {
								y = this.renderTarget.height - y - h;
						}
						this.sx = x;
						this.sy = y;
						this.sw = w;
						this.sh = h;
						this.passEncoder.setScissorRect(x, y, w, h);
				}
		}
		clearStorageBuffer(storageBuffer, offset = 0, size = storageBuffer.byteSize) {
				const commandEncoder = this.getCommandEncoder();
				commandEncoder.clearBuffer(storageBuffer.buffer, offset, size);
		}
		readStorageBuffer(storageBuffer, offset = 0, size = storageBuffer.byteSize - offset, data = null, immediate = false) {
				const stagingBuffer = this.createBufferImpl(BUFFERUSAGE_READ | BUFFERUSAGE_COPY_DST);
				stagingBuffer.allocate(this, size);
				const destBuffer = stagingBuffer.buffer;
				const commandEncoder = this.getCommandEncoder();
				commandEncoder.copyBufferToBuffer(storageBuffer.buffer, offset, destBuffer, 0, size);
				return this.readBuffer(stagingBuffer, size, data, immediate);
		}
		readBuffer(stagingBuffer, size, data = null, immediate = false) {
				const destBuffer = stagingBuffer.buffer;
				return new Promise((resolve, reject)=>{
						const read = ()=>{
								destBuffer?.mapAsync(GPUMapMode.READ).then(()=>{
										data ??= new Uint8Array(size);
										const copySrc = destBuffer.getMappedRange(0, size);
										const srcType = data.constructor;
										data.set(new srcType(copySrc));
										destBuffer.unmap();
										stagingBuffer.destroy(this);
										resolve(data);
								});
						};
						if (immediate) {
								this.submit();
								read();
						} else {
								setTimeout(()=>{
										read();
								});
						}
				});
		}
		writeStorageBuffer(storageBuffer, bufferOffset = 0, data, dataOffset = 0, size) {
				this.wgpu.queue.writeBuffer(storageBuffer.buffer, bufferOffset, data, dataOffset, size);
		}
		copyRenderTarget(source, dest, color, depth) {
				const copySize = {
						width: source ? source.width : dest.width,
						height: source ? source.height : dest.height,
						depthOrArrayLayers: 1
				};
				const commandEncoder = this.getCommandEncoder();
				if (color) {
						const copySrc = {
								texture: source ? source.colorBuffer.impl.gpuTexture : this.backBuffer.impl.assignedColorTexture,
								mipLevel: 0
						};
						const copyDst = {
								texture: dest ? dest.colorBuffer.impl.gpuTexture : this.backBuffer.impl.assignedColorTexture,
								mipLevel: 0
						};
						commandEncoder.copyTextureToTexture(copySrc, copyDst, copySize);
				}
				if (depth) {
						const sourceRT = source ? source : this.renderTarget;
						const sourceTexture = sourceRT.impl.depthAttachment.depthTexture;
						if (source.samples > 1) {
								const destTexture = dest.colorBuffer.impl.gpuTexture;
								this.resolver.resolveDepth(commandEncoder, sourceTexture, destTexture);
						} else {
								const destTexture = dest ? dest.depthBuffer.impl.gpuTexture : this.renderTarget.impl.depthAttachment.depthTexture;
								const copySrc = {
										texture: sourceTexture,
										mipLevel: 0
								};
								const copyDst = {
										texture: destTexture,
										mipLevel: 0
								};
								commandEncoder.copyTextureToTexture(copySrc, copyDst, copySize);
						}
				}
				return true;
		}
}

export { WebgpuGraphicsDevice };
