|
| 1 | +/* |
| 2 | + * Copyright 2026 Diligent Graphics LLC |
| 3 | + * |
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | + * you may not use this file except in compliance with the License. |
| 6 | + * You may obtain a copy of the License at |
| 7 | + * |
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | + * |
| 10 | + * Unless required by applicable law or agreed to in writing, software |
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | + * See the License for the specific language governing permissions and |
| 14 | + * limitations under the License. |
| 15 | + * |
| 16 | + * In no event and under no legal theory, whether in tort (including negligence), |
| 17 | + * contract, or otherwise, unless required by applicable law (such as deliberate |
| 18 | + * and grossly negligent acts) or agreed to in writing, shall any Contributor be |
| 19 | + * liable for any damages, including any direct, indirect, special, incidental, |
| 20 | + * or consequential damages of any character arising as a result of this License or |
| 21 | + * out of the use or inability to use the software (including but not limited to damages |
| 22 | + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and |
| 23 | + * all other commercial damages or losses), even if such Contributor has been advised |
| 24 | + * of the possibility of such damages. |
| 25 | + */ |
| 26 | + |
| 27 | +#pragma once |
| 28 | + |
| 29 | +/// \file |
| 30 | +/// Defines reference-counted CPU data storage with shared read and exclusive write access. |
| 31 | + |
| 32 | +#include "RadientTypes.h" |
| 33 | +#include "../../../DiligentCore/Primitives/interface/Object.h" |
| 34 | + |
| 35 | +DILIGENT_BEGIN_NAMESPACE(Diligent) |
| 36 | + |
| 37 | +typedef struct IRadientDataBlob IRadientDataBlob; |
| 38 | + |
| 39 | +/// Notification that a blob's reader count has changed from one to zero. |
| 40 | +/// |
| 41 | +/// \param [in] pBlob - Blob whose final reader released access. The blob stays |
| 42 | +/// alive throughout the callback. Retaining it after the |
| 43 | +/// callback requires acquiring a separate strong reference. |
| 44 | +/// \param [in] pUserData - The value supplied in RadientDataBlobCreateInfo::pUserData. |
| 45 | +/// |
| 46 | +/// Called synchronously by EndRead on the thread releasing the final reader, |
| 47 | +/// outside the internal access lock. The callback can call blob methods and |
| 48 | +/// acquire its own read or write access. This notification does not reserve |
| 49 | +/// write access: another thread may already have acquired access when it runs. |
| 50 | +/// New read cycles can cause callbacks to overlap across threads or nest on the |
| 51 | +/// same thread, so callback code and its user data account for this concurrency. |
| 52 | +/// If a C++ callback throws, EndRead catches the exception and returns |
| 53 | +/// RADIENT_STATUS_FAILED; the read access has already been released. |
| 54 | +typedef void (*RadientDataBlobReadReleaseCallbackType)(IRadientDataBlob* pBlob, void* pUserData); |
| 55 | + |
| 56 | +/// Creation parameters for a fixed-size CPU data blob. |
| 57 | +/// The size, callback, and user-data pointer are copied during creation. |
| 58 | +/// Optional initial bytes are copied before CreateRadientDataBlob returns. |
| 59 | +struct RadientDataBlobCreateInfo |
| 60 | +{ |
| 61 | + /// Storage size in bytes. The allocation keeps this size for the blob's |
| 62 | + /// lifetime. Initial contents come from pInitialData when provided, otherwise |
| 63 | + /// the allocation is zero-filled. Zero creates a valid empty blob, whose |
| 64 | + /// successful BeginRead and BeginWrite calls return null data pointers. |
| 65 | + /// Sizes beyond the implementation's addressable allocation range are |
| 66 | + /// rejected with RADIENT_STATUS_INVALID_ARGUMENT. The default is zero. |
| 67 | + Uint64 Size DEFAULT_INITIALIZER(0); |
| 68 | + |
| 69 | + /// Optional pointer to at least Size readable bytes used to initialize the |
| 70 | + /// allocation. CreateRadientDataBlob copies these bytes before returning; |
| 71 | + /// the pointer is not retained, and the caller can then modify or release |
| 72 | + /// the source memory. Initialization does not acquire an access scope or |
| 73 | + /// invoke OnLastReaderReleased. Ignored when Size is zero. The default is |
| 74 | + /// nullptr, which initializes the allocation to zero. |
| 75 | + const void* pInitialData DEFAULT_INITIALIZER(nullptr); |
| 76 | + |
| 77 | + /// Optional callback invoked once for each reader-count transition from one |
| 78 | + /// to zero. Creation, EndWrite, failed access attempts, and destruction do |
| 79 | + /// not invoke it. Registration remains fixed for the blob's lifetime. |
| 80 | + /// The default is nullptr, which disables notifications. |
| 81 | + RadientDataBlobReadReleaseCallbackType OnLastReaderReleased DEFAULT_INITIALIZER(nullptr); |
| 82 | + |
| 83 | + /// Opaque callback context, passed unchanged to OnLastReaderReleased. |
| 84 | + /// The blob stores the pointer without copying or owning the pointed-to data. |
| 85 | + /// When a callback is registered, this context remains valid throughout the |
| 86 | + /// blob's lifetime, including callback execution. May be null; defaults to |
| 87 | + /// nullptr. Ignored when OnLastReaderReleased is null. |
| 88 | + void* pUserData DEFAULT_INITIALIZER(nullptr); |
| 89 | +}; |
| 90 | +typedef struct RadientDataBlobCreateInfo RadientDataBlobCreateInfo; |
| 91 | + |
| 92 | +// {8A49B839-9F71-45AF-B8C0-058E75274F7B} |
| 93 | +static DILIGENT_CONSTEXPR INTERFACE_ID IID_RadientDataBlob = |
| 94 | + {0x8a49b839, 0x9f71, 0x45af, {0xb8, 0xc0, 0x05, 0x8e, 0x75, 0x27, 0x4f, 0x7b}}; |
| 95 | + |
| 96 | +#define DILIGENT_INTERFACE_NAME IRadientDataBlob |
| 97 | +#include "../../../DiligentCore/Primitives/interface/DefineInterfaceHelperMacros.h" |
| 98 | + |
| 99 | +#define IRadientDataBlobInclusiveMethods \ |
| 100 | + IObjectInclusiveMethods; \ |
| 101 | + IRadientDataBlobMethods RadientDataBlob |
| 102 | + |
| 103 | +// clang-format off |
| 104 | + |
| 105 | +/// Reference-counted CPU bytes with concurrent readers or one exclusive writer. |
| 106 | +/// |
| 107 | +/// All methods are thread-safe. Access acquisition returns an error on conflict |
| 108 | +/// without waiting for active readers or a writer to finish. Successful Begin |
| 109 | +/// calls are paired with exactly one corresponding End call. Access scopes are |
| 110 | +/// not tied to a thread; a caller can hand a scope to another thread using its |
| 111 | +/// own synchronization. Ending another caller's scope is invalid usage. |
| 112 | +/// |
| 113 | +/// Data pointers are usable only within their corresponding access scope. A |
| 114 | +/// caller retains a strong object reference until it finishes accessing the |
| 115 | +/// bytes and ends the scope; BeginRead and BeginWrite do not retain the blob. |
| 116 | +/// Read access does not permit modifying the bytes. Writes completed before |
| 117 | +/// EndWrite are visible to subsequent successful BeginRead and BeginWrite calls. |
| 118 | +/// Access methods synchronize scopes; they do not make stale pointer use safe. |
| 119 | +DILIGENT_BEGIN_INTERFACE(IRadientDataBlob, IObject) |
| 120 | +{ |
| 121 | + /// Returns the immutable allocation size in bytes, including during active |
| 122 | + /// read or write access. Does not acquire access or invoke the callback. |
| 123 | + VIRTUAL Uint64 METHOD(GetSize)(THIS) CONST PURE; |
| 124 | + |
| 125 | + /// Acquires one shared read scope and returns the first byte through ppData. |
| 126 | + /// Additional readers are allowed. Returns RADIENT_STATUS_INVALID_OPERATION |
| 127 | + /// while a writer is active or if the reader counter cannot be incremented. |
| 128 | + /// A null ppData returns RADIENT_STATUS_INVALID_ARGUMENT. On any failure, |
| 129 | + /// a non-null ppData is set to nullptr and access state is unchanged. |
| 130 | + /// Returns RADIENT_STATUS_OK on success, including for an empty blob. |
| 131 | + VIRTUAL RADIENT_STATUS METHOD(BeginRead)(THIS_ |
| 132 | + const void** ppData) PURE; |
| 133 | + |
| 134 | + /// Releases one read scope after the caller has finished reading its bytes. |
| 135 | + /// Returns RADIENT_STATUS_INVALID_OPERATION without changing state when |
| 136 | + /// there are no readers. Otherwise returns RADIENT_STATUS_OK, invoking |
| 137 | + /// OnLastReaderReleased if this releases the final reader. The callback |
| 138 | + /// completes before EndRead returns. A callback exception produces |
| 139 | + /// RADIENT_STATUS_FAILED with the read scope already released. |
| 140 | + VIRTUAL RADIENT_STATUS METHOD(EndRead)(THIS) PURE; |
| 141 | + |
| 142 | + /// Acquires exclusive write access and returns the first byte through ppData. |
| 143 | + /// Returns RADIENT_STATUS_INVALID_OPERATION while any reader or writer is |
| 144 | + /// active, including a second BeginWrite from the current writer. A null |
| 145 | + /// ppData returns RADIENT_STATUS_INVALID_ARGUMENT. On any failure, a non-null |
| 146 | + /// ppData is set to nullptr and access state is unchanged. Returns |
| 147 | + /// RADIENT_STATUS_OK on success, including for an empty blob. |
| 148 | + VIRTUAL RADIENT_STATUS METHOD(BeginWrite)(THIS_ |
| 149 | + void** ppData) PURE; |
| 150 | + |
| 151 | + /// Releases exclusive access after the caller has finished writing its bytes. |
| 152 | + /// Returns RADIENT_STATUS_OK when a writer was active, making those writes |
| 153 | + /// visible to subsequent access scopes. Otherwise returns |
| 154 | + /// RADIENT_STATUS_INVALID_OPERATION without changing state. Does not invoke |
| 155 | + /// OnLastReaderReleased. |
| 156 | + VIRTUAL RADIENT_STATUS METHOD(EndWrite)(THIS) PURE; |
| 157 | +}; |
| 158 | +DILIGENT_END_INTERFACE |
| 159 | + |
| 160 | +#include "../../../DiligentCore/Primitives/interface/UndefInterfaceHelperMacros.h" |
| 161 | + |
| 162 | +#if DILIGENT_C_INTERFACE |
| 163 | +# define IRadientDataBlob_GetSize(This) CALL_IFACE_METHOD(RadientDataBlob, GetSize, This) |
| 164 | +# define IRadientDataBlob_BeginRead(This, ...) CALL_IFACE_METHOD(RadientDataBlob, BeginRead, This, __VA_ARGS__) |
| 165 | +# define IRadientDataBlob_EndRead(This) CALL_IFACE_METHOD(RadientDataBlob, EndRead, This) |
| 166 | +# define IRadientDataBlob_BeginWrite(This, ...) CALL_IFACE_METHOD(RadientDataBlob, BeginWrite, This, __VA_ARGS__) |
| 167 | +# define IRadientDataBlob_EndWrite(This) CALL_IFACE_METHOD(RadientDataBlob, EndWrite, This) |
| 168 | +#endif |
| 169 | + |
| 170 | +// clang-format on |
| 171 | + |
| 172 | +#include "../../../DiligentCore/Primitives/interface/DefineGlobalFuncHelperMacros.h" |
| 173 | + |
| 174 | +/// Creates a CPU data blob without requiring a Radient engine or graphics device. |
| 175 | +/// |
| 176 | +/// \param [in] CreateInfo - Size, optional initial data, and last-reader notification. |
| 177 | +/// \param [out] ppBlob - Receives an owning reference on success. Must be |
| 178 | +/// non-null and point to a null interface pointer. |
| 179 | +/// |
| 180 | +/// Returns RADIENT_STATUS_OK on success, RADIENT_STATUS_INVALID_ARGUMENT for a |
| 181 | +/// null output pointer or an unsupported allocation size, or RADIENT_STATUS_FAILED |
| 182 | +/// if allocation fails. On failure, a non-null ppBlob is set to nullptr. |
| 183 | +/// C callers use Diligent_CreateRadientDataBlob and pass a CreateInfo pointer; |
| 184 | +/// a null CreateInfo pointer returns RADIENT_STATUS_INVALID_ARGUMENT. |
| 185 | +RADIENT_STATUS DILIGENT_GLOBAL_FUNCTION(CreateRadientDataBlob)(const RadientDataBlobCreateInfo REF CreateInfo, |
| 186 | + IRadientDataBlob** ppBlob); |
| 187 | + |
| 188 | +#include "../../../DiligentCore/Primitives/interface/UndefGlobalFuncHelperMacros.h" |
| 189 | + |
| 190 | +DILIGENT_END_NAMESPACE // namespace Diligent |
0 commit comments