import _ from 'lodash';
import '../../../../globals.js';
// Copyright 2023-2024, University of Colorado Boulder
/**
 * TODO: doc
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 */
import Utils from '../../../../dot/js/Utils.js';
import { optionize3 } from '../../../../phet-core/js/optionize.js';
import { alpenglow, BufferLogger, ComputePass, ConsoleLogger, webgpu } from '../../imports.js';
const EXECUTOR_DEFAULT_OPTIONS = {
    getTimestampWrites: _.constant(null),
    logBuffer: null
};
export default class Executor {
    constructor(deviceContext, encoder, bufferLogger, providedOptions) {
        this.deviceContext = deviceContext;
        this.encoder = encoder;
        this.bufferLogger = bufferLogger;
        const options = optionize3()({}, EXECUTOR_DEFAULT_OPTIONS, providedOptions);
        this.getTimestampWrites = options.getTimestampWrites;
    }
    getComputePass(name) {
        const computePassDescriptor = {
            label: `${name} compute pass`
        };
        const timestampWrites = this.getTimestampWrites(name);
        if (timestampWrites !== null) {
            computePassDescriptor.timestampWrites = timestampWrites;
        }
        return new ComputePass(this.encoder, computePassDescriptor);
    }
    setTypedBufferValue(typedBuffer, value) {
        typedBuffer.setValue(this.deviceContext.device, value);
    }
    async getTypedBufferValue(typedBuffer) {
        return typedBuffer.getValue(this.encoder, this.bufferLogger);
    }
    async arrayBuffer(buffer) {
        return this.bufferLogger.arrayBuffer(this.encoder, buffer);
    }
    async u32(buffer) {
        return this.bufferLogger.u32(this.encoder, buffer);
    }
    async i32(buffer) {
        return this.bufferLogger.i32(this.encoder, buffer);
    }
    async f32(buffer) {
        return this.bufferLogger.f32(this.encoder, buffer);
    }
    async u32Numbers(buffer) {
        return this.bufferLogger.u32Numbers(this.encoder, buffer);
    }
    async i32Numbers(buffer) {
        return this.bufferLogger.i32Numbers(this.encoder, buffer);
    }
    async f32Numbers(buffer) {
        return this.bufferLogger.f32Numbers(this.encoder, buffer);
    }
    static async execute(deviceContext, task, options) {
        const encoder = webgpu.deviceCreateCommandEncoder(deviceContext.device, { label: 'the encoder' });
        const bufferLogger = new BufferLogger(deviceContext);
        const executor = new Executor(deviceContext, encoder, bufferLogger, options);
        const logBuffer = options?.logBuffer || null;
        // TODO: staging ring for our "out" buffers?
        const outputPromise = task(executor);
        const logPromise = logBuffer ? executor.arrayBuffer(logBuffer) : Promise.resolve(null);
        const commandBuffer = webgpu.encoderFinish(encoder);
        webgpu.deviceQueueSubmit(deviceContext.device, [commandBuffer]);
        await bufferLogger.complete();
        const logResult = await logPromise;
        if (logResult) {
            const data = new Uint32Array(logResult);
            const length = data[0];
            const usedMessage = `logging used ${length} of ${data.length - 1} u32s (${Utils.roundSymmetric(100 * length / (data.length - 1))}%)`;
            console.log(usedMessage);
            const logData = ConsoleLogger.analyze(logResult);
            logData.forEach(shaderData => {
                shaderData.logLines.forEach(lineData => {
                    console.log(shaderData.shaderName, `>>> ${lineData.info.logName}${lineData.additionalIndex !== null ? ` (${lineData.additionalIndex})` : ''}`, lineData.info.lineToLog(lineData));
                });
            });
            console.log(usedMessage);
        }
        return outputPromise;
    }
}
alpenglow.register('Executor', Executor);
//# sourceMappingURL=Executor.js.map