#version 450
#extension GL_EXT_buffer_reference : require
#extension GL_EXT_buffer_reference_uvec2 : require
#extension GL_GOOGLE_include_directive : require
#include "../../../include/shader-debug/debug_channel.h"

layout(local_size_x_id = 4) in;

struct Command
{
	uint type;
	uint src_offset;
	uint dst_offset;
};

layout(buffer_reference, std430, buffer_reference_align = 4) readonly buffer Commands
{
	Command commands[];
};

layout(buffer_reference, std430, buffer_reference_align = 4) readonly buffer SrcBuffer {
	uint values[];
};

layout(buffer_reference, std430, buffer_reference_align = 4) writeonly buffer DstBuffer {
	uint values[];
};

layout(buffer_reference, std430, buffer_reference_align = 4) readonly buffer IndirectCount {
	uint count;
};

layout(buffer_reference, std430, buffer_reference_align = 4) writeonly buffer IndirectCountWrite {
	uint count;
};

layout(push_constant) uniform Registers
{
	Commands commands_va;
	SrcBuffer src_buffer_va;
	DstBuffer dst_buffer_va;
	uvec2 indirect_count_va;
	IndirectCountWrite dst_indirect_count_va;
	uint src_stride;
	uint dst_stride;

	// Debug metadata here
	uint debug_tag;
    uint implicit_instance;
};

void main()
{
    if (debug_tag != 0u)
        DEBUG_CHANNEL_INIT_IMPLICIT_INSTANCE(uvec3(debug_tag, gl_WorkGroupID.x, gl_LocalInvocationIndex), implicit_instance);

    Command cmd = commands_va.commands[gl_LocalInvocationIndex];

    uint draw_id = gl_WorkGroupID.x;
    uint max_draws = gl_NumWorkGroups.x;
    if (any(notEqual(indirect_count_va, uvec2(0))))
    {
        max_draws = min(max_draws, IndirectCount(indirect_count_va).count);
        if (gl_WorkGroupID.x == 0u)
            dst_indirect_count_va.count = max_draws;
    }

    if (debug_tag != 0u && gl_WorkGroupID.x == 0)
        DEBUG_CHANNEL_MSG_UNIFORM(int(max_draws), int(gl_NumWorkGroups.x));

    if (draw_id < max_draws)
    {
        uint src_offset = src_stride * draw_id + cmd.src_offset;
        uint dst_offset = dst_stride * draw_id + cmd.dst_offset;

        uint src_value = src_buffer_va.values[src_offset];

        if (debug_tag != 0u)
            DEBUG_CHANNEL_MSG(cmd.type, dst_offset, src_offset, src_value);

        dst_buffer_va.values[dst_offset] = src_value;
    }
}
