/*
 * This file is part of libplacebo.
 *
 * libplacebo is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * libplacebo is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with libplacebo.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef LIBPLACEBO_DISPATCH_H_
#define LIBPLACEBO_DISPATCH_H_

#include <libplacebo/shaders.h>
#include <libplacebo/gpu.h>

PL_API_BEGIN

// Thread-safety: Safe
typedef PL_STRUCT(pl_dispatch) *pl_dispatch;

// Creates a new shader dispatch object. This object provides a translation
// layer between generated shaders (pl_shader) and the ra context such that it
// can be used to execute shaders. This dispatch object will also provide
// shader caching (for efficient re-use).
pl_dispatch pl_dispatch_create(pl_log log, pl_gpu gpu);
void pl_dispatch_destroy(pl_dispatch *dp);

// Reset/increments the internal counters of the pl_dispatch. This must be
// called whenever the user is going to begin with a new frame, in order to
// perform garbage collection and advance the state of the internal PRNG.
//
// Note that shaders generated by `pl_dispatch` are therefore entirely
// deterministic, as long as the sequence of calls (and inputs to the shader)
// are the same.
void pl_dispatch_reset_frame(pl_dispatch dp);

// Returns a blank pl_shader object, suitable for recording rendering commands.
// For more information, see the header documentation in `shaders/*.h`.
pl_shader pl_dispatch_begin(pl_dispatch dp);

// Struct passed to `info_callback`. Only valid until that function returns.
struct pl_dispatch_info {
    // The finalized/generated shader for this shader execution, as well
    // as a signature uniquely identifying it.
    const struct pl_shader_res *shader;
    uint64_t signature;

    // A list of execution times for this pass, in nanoseconds. May be empty.
    uint64_t samples[256];
    int num_samples;

    // As a convenience, this contains the last, average and peak of the above
    // list of samples. If `num_samples` is 0, these values are also 0.
    uint64_t last;
    uint64_t peak;
    uint64_t average;
};

// Set up a dispatch callback for this `pl_dispatch` object. The given callback
// will be run for every successfully dispatched shader. Call this again with
// `cb == NULL` to disable.
void pl_dispatch_callback(pl_dispatch dp, void *priv,
                          void (*cb)(void *priv, const struct pl_dispatch_info *));

struct pl_dispatch_params {
    // The shader to execute. The pl_dispatch will take over ownership
    // of this shader, and return it back to the internal pool.
    //
    // This shader must have a compatible signature, i.e. inputs
    // `PL_SHADER_SIG_NONE` and outputs `PL_SHADER_SIG_COLOR`.
    pl_shader *shader;

    // The texture to render to. This must have params compatible with the
    // shader, i.e. `target->params.renderable` for fragment shaders and
    // `target->params.storable` for compute shaders.
    //
    // Note: Even when not using compute shaders, users are advised to always
    // set `target->params.storable` if permitted by the `pl_fmt`, since this
    // allows the use of compute shaders instead of full-screen quads, which is
    // faster on some platforms.
    pl_tex target;

    // The target rect to render to. Optional, if left as {0}, then the
    // entire texture will be rendered to.
    struct pl_rect2d rect;

    // If set, enables and controls the blending for this pass. Optional. When
    // using this with fragment shaders, `target->params.fmt->caps` must
    // include `PL_FMT_CAP_BLENDABLE`.
    const struct pl_blend_params *blend_params;

    // If set, records the execution time of this dispatch into the given
    // timer object. Optional.
    //
    // Note: If this is set, `pl_dispatch` cannot internally measure the
    // execution time of the shader, which means `pl_dispatch_info.samples` may
    // be empty as a result.
    pl_timer timer;
};

#define pl_dispatch_params(...) (&(struct pl_dispatch_params) { __VA_ARGS__ })

// Dispatch a generated shader (via the pl_shader mechanism). Returns whether
// or not the dispatch was successful.
bool pl_dispatch_finish(pl_dispatch dp, const struct pl_dispatch_params *params);

struct pl_dispatch_compute_params {
    // The shader to execute. This must be a compute shader with the input
    // set to PL_SHADER_SIG_NONE. The output, if it has any, is ignored.
    pl_shader *shader;

    // The number of work groups to dispatch in each dimension. If this is left
    // as [0} and `width/height` are both set, the number of work groups will
    // be inferred from the shader's `compute_group_sizes`.
    int dispatch_size[3];

    // If set, simulate vertex attributes (similar to `pl_dispatch_finish`)
    // according to the given dimensions. The first two components of the
    // thread's global ID will be interpreted as the X and Y locations.
    //
    // Optional, ignored if either component is left as 0.
    int width, height;

    // If set, records the execution time of this dispatch into the given
    // timer object. Optional.
    //
    // Note: If this is set, `pl_dispatch` cannot internally measure the
    // execution time of the shader, which means `pl_dispatch_info.samples` may
    // be empty as a result.
    pl_timer timer;
};

#define pl_dispatch_compute_params(...) (&(struct pl_dispatch_compute_params) { __VA_ARGS__ })

// A variant of `pl_dispatch_finish`, this one only dispatches a compute shader
// while ignoring its output (if it has one). It's only useful for shaders
// which have otherwise observable side effects (such as updating state
// objects).
bool pl_dispatch_compute(pl_dispatch dp, const struct pl_dispatch_compute_params *params);

enum pl_vertex_coords {
    PL_COORDS_ABSOLUTE,     // Absolute/integer `target` coordinates
    PL_COORDS_RELATIVE,     // Relative `target` coordinates in range [0, 1]
    PL_COORDS_NORMALIZED,   // GL-normalized coordinates in range  [-1, 1]
};

struct pl_dispatch_vertex_params {
    // The shader to execute. This must be a raster shader with the input set
    // to `PL_SHADER_SIG_NONE` and the output set to `PL_SHADER_SIG_COLOR`.
    //
    // Additionally, the shader must not have any attached vertex attributes.
    pl_shader *shader;

    // The texture to render to. Requires `target->params.renderable`.
    pl_tex target;

    // The target rect to clip the rendering to. (Optional)
    struct pl_rect2d scissors;

    // If set, enables and controls the blending for this pass. Optional. When
    // enabled, `target->params.fmt->caps` must include `PL_FMT_CAP_BLENDABLE`.
    const struct pl_blend_params *blend_params;

    // The description of the vertex format, including offsets.
    //
    // Note: `location` is ignored and can safely be left unset.
    const struct pl_vertex_attrib *vertex_attribs;
    int num_vertex_attribs;
    size_t vertex_stride;

    // The index of the vertex position in `vertex_attribs`, as well as the
    // interpretation of its contents.
    int vertex_position_idx;
    enum pl_vertex_coords vertex_coords;
    bool vertex_flipped; // flip all vertex y coordinates

    // Type and number of vertices to render.
    enum pl_prim_type vertex_type;
    int vertex_count;

    // Vertex data. See `pl_pass_run_params.vertex_data`.
    const void *vertex_data;
    pl_buf vertex_buf;
    size_t buf_offset;

    // Index data. See `pl_pass_run_params.index_data`. Optional.
    const void *index_data;
    enum pl_index_format index_fmt;
    pl_buf index_buf;
    size_t index_offset;

    // If set, records the execution time of this dispatch into the given
    // timer object. Optional.
    //
    // Note: If this is set, `pl_dispatch` cannot internally measure the
    // execution time of the shader, which means `pl_dispatch_info.samples` may
    // be empty as a result.
    pl_timer timer;
};

#define pl_dispatch_vertex_params(...) (&(struct pl_dispatch_vertex_params) { __VA_ARGS__ })

// Dispatch a generated shader using custom vertices, rather than using a quad
// generated by the dispatch. This allows the use of e.g. custom fragment
// shaders for things like rendering custom UI elements, or possibly doing
// advanced things like sampling from a cube map or spherical video.
bool pl_dispatch_vertex(pl_dispatch dp, const struct pl_dispatch_vertex_params *params);

// Cancel an active shader without submitting anything. Useful, for example,
// if the shader was instead merged into a different shader.
void pl_dispatch_abort(pl_dispatch dp, pl_shader *sh);

// Serialize the internal state of a `pl_dispatch` into an abstract cache
// object that can be e.g. saved to disk and loaded again later. This function
// will not truncate, so the buffer provided by the user must be large enough
// to contain the entire output. Returns the number of bytes written to
// `out_cache`, or the number of bytes that *would* have been written to
// `out_cache` if it's NULL.
size_t pl_dispatch_save(pl_dispatch dp, uint8_t *out_cache);

// Load the result of a previous `pl_dispatch_save` call. This function will
// never fail. It doesn't forget about any existing shaders, but merely
// initializes an internal state cache needed to more efficiently compile
// shaders that are not yet present in the `pl_dispatch`.
//
// Note: See the security warnings on `pl_pass_params.cached_program`.
void pl_dispatch_load(pl_dispatch dp, const uint8_t *cache);

PL_API_END

#endif // LIBPLACEBO_DISPATCH_H
