新增taskflow, 新增单例管理, 新增从Slate版本迁移的部分功能
This commit is contained in:
parent
2f80e79800
commit
8b328cc0d6
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -10,3 +10,6 @@
|
||||
[submodule "third_party/glfw"]
|
||||
path = third_party/glfw
|
||||
url = https://github.com/glfw/glfw.git
|
||||
[submodule "third_party/taskflow"]
|
||||
path = third_party/taskflow
|
||||
url = https://github.com/taskflow/taskflow.git
|
||||
|
@ -96,6 +96,8 @@ add_subdirectory(third_party/imgui)
|
||||
add_subdirectory(third_party/portaudio)
|
||||
add_subdirectory(third_party/spdlog)
|
||||
add_subdirectory(third_party/glfw)
|
||||
add_subdirectory(third_party/mempool)
|
||||
add_subdirectory(third_party/taskflow)
|
||||
|
||||
# setup portaudio
|
||||
set(PA_USE_ASIO ON CACHE BOOL "" FORCE)
|
||||
@ -121,6 +123,10 @@ set(SPDLOG_WCHAR_SUPPORT ON CACHE BOOL "" FORCE)
|
||||
set(SPDLOG_ENABLE_PCH ON CACHE BOOL "" FORCE)
|
||||
set(SPDLOG_USE_STD_FORMAT OFF CACHE BOOL "" FORCE)
|
||||
|
||||
# setup taskflow
|
||||
set(TF_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
||||
set(TF_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
|
||||
|
||||
add_definitions(-DGLFW_INCLUDE_NONE)
|
||||
|
||||
# install
|
||||
|
@ -8,10 +8,10 @@ retrieve_files(ALL_FILES)
|
||||
add_library(${PROJECT_NAME} SHARED ${ALL_FILES})
|
||||
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} spdlog imgui glfw)
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} PortAudio spdlog imgui glfw mempool Taskflow)
|
||||
|
||||
target_link_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} spdlog imgui glfw)
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC imgui spdlog glfw)
|
||||
target_link_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} PortAudio spdlog imgui glfw mempool Taskflow)
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC PortAudio imgui spdlog glfw mempool Taskflow)
|
||||
|
||||
target_precompile_headers(${PROJECT_NAME} PUBLIC extern.h)
|
||||
add_definitions(-Dcore_EXPORTS -DSTB_IMAGE_IMPLEMENTATION -DSTBI_WINDOWS_UTF8)
|
||||
|
@ -48,8 +48,11 @@ void application::init(const window_params& in_window_params, int argc, char** a
|
||||
}
|
||||
|
||||
int application::run() {
|
||||
const ImGuiIO& io = ImGui::GetIO();
|
||||
while (!g_exit_requested) {
|
||||
glfwPollEvents();
|
||||
g_exit_requested = g_exit_requested || glfwWindowShouldClose(window_);
|
||||
tick(io.DeltaTime);
|
||||
renderer_->new_frame(window_);
|
||||
draw_gui();
|
||||
renderer_->end_frame(window_);
|
||||
@ -71,7 +74,7 @@ void application::shutdown() {
|
||||
g_is_running = false;
|
||||
}
|
||||
|
||||
std::shared_ptr<texture> application::load_texture(const std::string& path, vk::Format format) const {
|
||||
std::shared_ptr<texture> application::load_texture(const std::string& path, vk::Format format) {
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
uint8_t* image_data = stbi_load(path.c_str(), &width, &height, nullptr, 4);
|
||||
@ -84,27 +87,17 @@ std::shared_ptr<texture> application::load_texture(const std::string& path, vk::
|
||||
return texture;
|
||||
}
|
||||
|
||||
std::shared_ptr<texture>
|
||||
application::create_texture(const unsigned char* data, const int width, const int height, vk::Format format) const {
|
||||
std::shared_ptr<texture> application::create_texture(const unsigned char* data, const int width, const int height, vk::Format format) {
|
||||
return renderer::create_texture(data, width, height, format);
|
||||
}
|
||||
|
||||
std::shared_ptr<render_target> application::create_render_target(const int width, const int height,
|
||||
vk::Format format) const {
|
||||
return renderer::create_render_target(width, height, format);
|
||||
}
|
||||
|
||||
std::shared_ptr<pixel_shader_drawer> application::create_pixel_shader_drawer() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void application::init_glfw() {
|
||||
glfwSetErrorCallback(glfw_error_callback);
|
||||
if (!glfwInit()) {
|
||||
spdlog::error("Failed to initialize GLFW");
|
||||
return;
|
||||
}
|
||||
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
|
||||
// glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
|
||||
}
|
||||
|
||||
void application::init_imgui() {
|
||||
@ -116,8 +109,8 @@ void application::init_imgui() {
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
|
||||
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
|
||||
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows
|
||||
io.ConfigViewportsNoAutoMerge = true;
|
||||
io.ConfigViewportsNoTaskBarIcon = false;
|
||||
// io.ConfigViewportsNoAutoMerge = true;
|
||||
// io.ConfigViewportsNoTaskBarIcon = false;
|
||||
ImGui::StyleColorsDark();
|
||||
//ImGui::StyleColorsLight();
|
||||
}
|
||||
|
@ -5,7 +5,6 @@
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
class renderer;
|
||||
class pixel_shader_drawer;
|
||||
class render_target;
|
||||
class texture;
|
||||
class application;
|
||||
@ -55,19 +54,14 @@ public:
|
||||
}
|
||||
|
||||
virtual void draw_gui() = 0;
|
||||
virtual void tick(float delta_time) {}
|
||||
|
||||
virtual const char* get_shader_path() = 0;
|
||||
|
||||
virtual void init_imgui(ImGuiContext* in_context) = 0;
|
||||
|
||||
[[nodiscard]] std::shared_ptr<texture> load_texture(const std::string& path, vk::Format format = vk::Format::eR8G8B8A8Unorm) const;
|
||||
|
||||
std::shared_ptr<texture> create_texture(const unsigned char* data, int width, int height, vk::Format format) const;
|
||||
|
||||
[[nodiscard]] std::shared_ptr<render_target> create_render_target(int width, int height,
|
||||
vk::Format format) const;
|
||||
|
||||
[[nodiscard]] std::shared_ptr<pixel_shader_drawer> create_pixel_shader_drawer() const;
|
||||
[[nodiscard]] static std::shared_ptr<texture> load_texture(const std::string& path, vk::Format format = vk::Format::eR8G8B8A8Unorm);
|
||||
[[nodiscard]] static std::shared_ptr<texture> create_texture(const unsigned char* data, int width, int height, vk::Format format);
|
||||
|
||||
[[nodiscard]] virtual const char* get_entry_model() const = 0;
|
||||
|
||||
|
1
core/audio/device/audio_device.cpp
Normal file
1
core/audio/device/audio_device.cpp
Normal file
@ -0,0 +1 @@
|
||||
#include "audio_device.h"
|
29
core/audio/device/audio_device.h
Normal file
29
core/audio/device/audio_device.h
Normal file
@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
class audio_device {
|
||||
public:
|
||||
virtual ~audio_device() = default;
|
||||
|
||||
virtual bool init() = 0;
|
||||
virtual void destroy() = 0;
|
||||
|
||||
void set_sample_rate(double sample_rate) {
|
||||
if (on_set_sample_rate(sample_rate)) {
|
||||
sample_rate_ = sample_rate;
|
||||
}
|
||||
}
|
||||
void set_buffer_size(uint32_t buffer_size) {
|
||||
if (on_set_buffer_size(buffer_size)) {
|
||||
buffer_size_ = buffer_size;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] double sample_rate() const { return sample_rate_; }
|
||||
[[nodiscard]] uint32_t buffer_size() const { return buffer_size_; }
|
||||
protected:
|
||||
virtual bool on_set_sample_rate(double in_sample_rate) = 0;
|
||||
virtual bool on_set_buffer_size(uint32_t in_buffer_size) = 0;
|
||||
private:
|
||||
double sample_rate_ = 44100;
|
||||
uint32_t buffer_size_ = 512;
|
||||
};
|
1
core/audio/device/dummy_audio_device.cpp
Normal file
1
core/audio/device/dummy_audio_device.cpp
Normal file
@ -0,0 +1 @@
|
||||
#include "dummy_audio_device.h"
|
12
core/audio/device/dummy_audio_device.h
Normal file
12
core/audio/device/dummy_audio_device.h
Normal file
@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include "audio_device.h"
|
||||
|
||||
class dummy_audio_device : public audio_device {
|
||||
public:
|
||||
bool init() override { return true; }
|
||||
void destroy() override {}
|
||||
|
||||
protected:
|
||||
bool on_set_buffer_size(uint32_t in_buffer_size) override { return true; }
|
||||
bool on_set_sample_rate(double in_sample_rate) override { return true; }
|
||||
};
|
127
core/audio/device/port_audio_device.cpp
Normal file
127
core/audio/device/port_audio_device.cpp
Normal file
@ -0,0 +1,127 @@
|
||||
#include "port_audio_device.h"
|
||||
|
||||
#include "audio/mixer/mixer.h"
|
||||
#include "audio/mixer/mixer_track.h"
|
||||
#include "audio/plugin_host/midi_sequencer.h"
|
||||
#include "audio/plugin_host/plugin_host_manager.h"
|
||||
#include "thread_message/thread_message_hubs.h"
|
||||
|
||||
int port_audio_callback(const void* Input, void* Output, unsigned long FrameCount, const PaStreamCallbackTimeInfo* TimeInfo, PaStreamCallbackFlags StatusFlags, void* UserData)
|
||||
{
|
||||
auto* API = static_cast<port_audio_device*>(UserData);
|
||||
return API->stream_callback((float**)Input, static_cast<float**>(Output), FrameCount, TimeInfo, StatusFlags);
|
||||
}
|
||||
|
||||
port_audio_device::~port_audio_device() {
|
||||
port_audio_device::destroy();
|
||||
}
|
||||
|
||||
bool port_audio_device::init() {
|
||||
Pa_Initialize();
|
||||
try {
|
||||
open_stream(0, Pa_GetDefaultOutputDevice(), sample_rate(), buffer_size());
|
||||
} catch (const std::exception& e) {
|
||||
spdlog::error("Init failed: {}", e.what());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void port_audio_device::destroy() {
|
||||
stop();
|
||||
Pa_Terminate();
|
||||
}
|
||||
|
||||
void port_audio_device::stop() {
|
||||
Pa_StopStream(stream_);
|
||||
Pa_CloseStream(stream_);
|
||||
stream_ = nullptr;
|
||||
render_thread_.join();
|
||||
}
|
||||
|
||||
void port_audio_device::open_stream(uint32_t input_device, uint32_t output_device, double in_sample_rate, uint32_t in_buffer_size) {
|
||||
PaStreamParameters* input_params = nullptr;
|
||||
if (input_device > 0) {
|
||||
input_params_.device = input_device;
|
||||
input_params_.channelCount = 2;
|
||||
input_params_.sampleFormat = paFloat32 | paNonInterleaved;
|
||||
input_params_.suggestedLatency = Pa_GetDeviceInfo(input_device)->defaultLowInputLatency;
|
||||
input_params_.hostApiSpecificStreamInfo = nullptr;
|
||||
input_params = &input_params_;
|
||||
}
|
||||
|
||||
output_params_.device = output_device;
|
||||
output_params_.channelCount = 2;
|
||||
output_params_.sampleFormat = paFloat32 | paNonInterleaved;
|
||||
output_params_.suggestedLatency = Pa_GetDeviceInfo(output_device)->defaultLowOutputLatency;
|
||||
output_params_.hostApiSpecificStreamInfo = nullptr;
|
||||
PaError err = Pa_OpenStream(&stream_, input_params, &output_params_, in_sample_rate, in_buffer_size, paClipOff, port_audio_callback, this);
|
||||
if (err != paNoError) {
|
||||
throw std::runtime_error(Pa_GetErrorText(err));
|
||||
}
|
||||
err = Pa_StartStream(stream_);
|
||||
if (err != paNoError) {
|
||||
throw std::runtime_error(Pa_GetErrorText(err));
|
||||
}
|
||||
render_thread_ = std::thread(&port_audio_device::render_thread, this);
|
||||
render_thread_.detach();
|
||||
}
|
||||
|
||||
int port_audio_device::stream_callback(float** input, float** output, unsigned long frame_count,
|
||||
const PaStreamCallbackTimeInfo* time_info, PaStreamCallbackFlags status_flags) {
|
||||
if (render_buffer_.Num() < frame_count * output_params_.channelCount)
|
||||
return paContinue;
|
||||
for (int i = 0; i < output_params_.channelCount; ++i) {
|
||||
render_buffer_.Pop(input[i], frame_count);
|
||||
}
|
||||
return paContinue;
|
||||
}
|
||||
|
||||
bool port_audio_device::on_set_buffer_size(uint32_t in_buffer_size) {
|
||||
stop();
|
||||
try {
|
||||
open_stream(input_params_.device, output_params_.device, sample_rate(), in_buffer_size);
|
||||
} catch (const std::exception& e) {
|
||||
spdlog::error("Set buffer size failed: {}", e.what());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool port_audio_device::on_set_sample_rate(double in_sample_rate) {
|
||||
stop();
|
||||
try {
|
||||
open_stream(input_params_.device, output_params_.device, in_sample_rate, buffer_size());
|
||||
} catch (const std::exception& e) {
|
||||
spdlog::error("Set sample rate failed: {}", e.what());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void port_audio_device::render_thread() {
|
||||
dummy_track* master = g_mixer.get_master();
|
||||
|
||||
|
||||
while (stream_) {
|
||||
const uint32_t frames = buffer_size();
|
||||
const double rate = sample_rate();
|
||||
|
||||
const uint32_t milliseconds = 1.f / (rate / (frames / 4)) * 1000;
|
||||
|
||||
g_audio_thread_hub.process_messages();
|
||||
if (render_buffer_.Num() >= render_buffer_size_) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
|
||||
continue;
|
||||
}
|
||||
g_midi_sequencer.process(rate, frames);
|
||||
g_mixer.reset();
|
||||
g_plugin_host_manager.process(frames);
|
||||
g_mixer.process(frames);
|
||||
|
||||
const auto& master_headers = master->buffer.get_headers_vector();
|
||||
for (const auto element: master_headers) {
|
||||
render_buffer_.Push(element, frames);
|
||||
}
|
||||
}
|
||||
}
|
33
core/audio/device/port_audio_device.h
Normal file
33
core/audio/device/port_audio_device.h
Normal file
@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
#include "audio_device.h"
|
||||
|
||||
#include "portaudio.h"
|
||||
#include "audio/misc/circular_audio_buffer.h"
|
||||
|
||||
class port_audio_device : public audio_device {
|
||||
public:
|
||||
~port_audio_device() override;
|
||||
bool init() override;
|
||||
void destroy() override;
|
||||
|
||||
void stop();
|
||||
|
||||
void open_stream(uint32_t input_device, uint32_t output_device, double in_sample_rate, uint32_t in_buffer_size);
|
||||
|
||||
int stream_callback(float** input, float** output, unsigned long frame_count, const PaStreamCallbackTimeInfo* time_info, PaStreamCallbackFlags status_flags);
|
||||
protected:
|
||||
bool on_set_buffer_size(uint32_t in_buffer_size) override;
|
||||
bool on_set_sample_rate(double in_sample_rate) override;
|
||||
private:
|
||||
|
||||
#pragma region render_thread
|
||||
void render_thread();
|
||||
std::thread render_thread_;
|
||||
circular_audio_buffer<float> render_buffer_;
|
||||
uint32_t render_buffer_size_ = 0;
|
||||
#pragma endregion
|
||||
|
||||
PaStreamParameters input_params_ = {};
|
||||
PaStreamParameters output_params_ = {};
|
||||
PaStream* stream_ = nullptr;
|
||||
};
|
40
core/audio/misc/audio_buffer.cpp
Normal file
40
core/audio/misc/audio_buffer.cpp
Normal file
@ -0,0 +1,40 @@
|
||||
#include "audio_buffer.h"
|
||||
|
||||
void audio_buffer::resize(uint32_t channel_num, uint32_t block_size) {
|
||||
std::scoped_lock lock(lock_);
|
||||
buffer_.resize(channel_num);
|
||||
headers_.resize(channel_num);
|
||||
for (uint32_t i = 0; i < channel_num; i++) {
|
||||
buffer_[i].resize(block_size);
|
||||
headers_[i] = buffer_[i].data();
|
||||
}
|
||||
}
|
||||
|
||||
void audio_buffer::clear() {
|
||||
std::scoped_lock lock(lock_);
|
||||
for (auto& channel : buffer_) {
|
||||
std::fill(channel.begin(), channel.end(), 0.f);
|
||||
}
|
||||
}
|
||||
|
||||
void audio_buffer::mix(audio_buffer& in_buffer, float percent) {
|
||||
std::scoped_lock lock(lock_);
|
||||
// will be optimized by compiler
|
||||
for (uint32_t channel_index = 0; channel_index < buffer_.size(); channel_index++) {
|
||||
auto& channel = buffer_[channel_index];
|
||||
auto& in_channel = in_buffer.buffer_[channel_index];
|
||||
for (uint32_t sample_index = 0; sample_index < channel.size(); sample_index++) {
|
||||
channel[sample_index] += in_channel[sample_index] * percent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void audio_buffer::multiple(float percent) {
|
||||
std::scoped_lock lock(lock_);
|
||||
// will be optimized by compiler
|
||||
for (auto& channel : buffer_) {
|
||||
for (auto& sample : channel) {
|
||||
sample *= percent;
|
||||
}
|
||||
}
|
||||
}
|
21
core/audio/misc/audio_buffer.h
Normal file
21
core/audio/misc/audio_buffer.h
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
class audio_buffer {
|
||||
public:
|
||||
float** get_headers() { return headers_.data(); }
|
||||
const std::vector<float*>& get_headers_vector() { return headers_; }
|
||||
|
||||
[[nodiscard]] uint32_t get_num_channels() const { return buffer_.size(); }
|
||||
[[nodiscard]] uint32_t get_num_samples() const { return buffer_[0].size(); }
|
||||
|
||||
void resize(uint32_t channel_num, uint32_t block_size);
|
||||
|
||||
void clear();
|
||||
void mix(audio_buffer& in_buffer, float percent = 1.f);
|
||||
void multiple(float percent);
|
||||
|
||||
private:
|
||||
std::vector<std::vector<float>> buffer_;
|
||||
std::vector<float*> headers_;
|
||||
std::mutex lock_;
|
||||
};
|
268
core/audio/misc/circular_audio_buffer.h
Normal file
268
core/audio/misc/circular_audio_buffer.h
Normal file
@ -0,0 +1,268 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
|
||||
#include "extern.h"
|
||||
|
||||
/**
|
||||
* Basic implementation of a circular buffer built for pushing and popping arbitrary amounts of data at once.
|
||||
* Designed to be thread safe for SPSC; However, if Push() and Pop() are both trying to access an overlapping area of the buffer,
|
||||
* One of the calls will be truncated. Thus, it is advised that you use a high enough capacity that the producer and consumer are never in contention.
|
||||
*/
|
||||
template<typename SampleType>
|
||||
class circular_audio_buffer {
|
||||
private:
|
||||
std::vector<SampleType> InternalBuffer;
|
||||
uint32_t Capacity;
|
||||
std::atomic<int32_t> ReadCounter;
|
||||
std::atomic<int32_t> WriteCounter;
|
||||
|
||||
public:
|
||||
circular_audio_buffer() {
|
||||
SetCapacity(0);
|
||||
}
|
||||
|
||||
circular_audio_buffer(const circular_audio_buffer& InOther) {
|
||||
*this = InOther;
|
||||
}
|
||||
|
||||
circular_audio_buffer& operator=(const circular_audio_buffer& InOther) {
|
||||
InternalBuffer = InOther.InternalBuffer;
|
||||
Capacity = InOther.Capacity;
|
||||
ReadCounter = InOther.ReadCounter;
|
||||
WriteCounter = InOther.WriteCounter;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
circular_audio_buffer(uint32_t InCapacity) {
|
||||
SetCapacity(InCapacity);
|
||||
}
|
||||
|
||||
void Reset(uint32_t InCapacity = 0) {
|
||||
SetCapacity(InCapacity);
|
||||
}
|
||||
|
||||
void SetCapacity(uint32_t InCapacity) {
|
||||
if (!(InCapacity < (uint32_t)2147483647i32)) { spdlog::error("Max capacity for this buffer is 2,147,483,647 samples. Otherwise our index arithmetic will not work."); throw std::runtime_error(fmt::format("Max capacity for this buffer is 2,147,483,647 samples. Otherwise our index arithmetic will not work.")); };
|
||||
|
||||
Capacity = InCapacity + 1;
|
||||
ReadCounter = 0;
|
||||
WriteCounter = 0;
|
||||
InternalBuffer.Reset();
|
||||
InternalBuffer.AddZeroed(Capacity);
|
||||
}
|
||||
|
||||
/** Reserve capacity.
|
||||
*
|
||||
* @param InMinimumCapacity - Minimum capacity of circular buffer.
|
||||
* @param bRetainExistingSamples - If true, existing samples in the buffer will be retained. If false, they are discarded.
|
||||
*/
|
||||
void Reserve(uint32_t InMinimumCapacity, bool bRetainExistingSamples) {
|
||||
if (Capacity <= InMinimumCapacity) {
|
||||
uint32_t NewCapacity = InMinimumCapacity + 1;
|
||||
|
||||
checkf(NewCapacity < (uint32_t)INT32_MAX,
|
||||
"Max capacity overflow. Requested {}. Maximum allowed {}", NewCapacity,
|
||||
INT32_MAX);
|
||||
|
||||
uint32_t NumToAdd = NewCapacity - Capacity;
|
||||
InternalBuffer.AddZeroed(NumToAdd);
|
||||
Capacity = NewCapacity;
|
||||
}
|
||||
|
||||
if (!bRetainExistingSamples) {
|
||||
ReadCounter = 0;
|
||||
WriteCounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** Push an array of values into circular buffer. */
|
||||
int32_t Push(std::vector<const SampleType> InBuffer) {
|
||||
return Push(InBuffer.data(), InBuffer.size());
|
||||
}
|
||||
|
||||
// Pushes some amount of samples into this circular buffer.
|
||||
// Returns the amount of samples written.
|
||||
// This can only be used for trivially copyable types.
|
||||
int32_t Push(const SampleType* InBuffer, uint32_t NumSamples) {
|
||||
SampleType* DestBuffer = InternalBuffer.data();
|
||||
const uint32_t ReadIndex = ReadCounter;
|
||||
const uint32_t WriteIndex = WriteCounter;
|
||||
|
||||
int32_t NumToCopy = std::min<int32_t>(NumSamples, Remainder());
|
||||
const int32_t NumToWrite = std::min<int32_t>(NumToCopy, Capacity - WriteIndex);
|
||||
|
||||
memcpy(&DestBuffer[WriteIndex], InBuffer, NumToWrite * sizeof(SampleType));
|
||||
memcpy(&DestBuffer[0], &InBuffer[NumToWrite], (NumToCopy - NumToWrite) * sizeof(SampleType));
|
||||
|
||||
WriteCounter = (WriteIndex + NumToCopy) % Capacity;
|
||||
|
||||
return NumToCopy;
|
||||
}
|
||||
|
||||
// Pushes some amount of zeros into the circular buffer.
|
||||
// Useful when acting as a blocked, mono/interleaved delay line
|
||||
int32_t PushZeros(uint32_t NumSamplesOfZeros) {
|
||||
SampleType* DestBuffer = InternalBuffer.data();
|
||||
const uint32_t ReadIndex = ReadCounter;
|
||||
const uint32_t WriteIndex = WriteCounter;
|
||||
|
||||
int32_t NumToZeroEnd = std::min<int32_t>(NumSamplesOfZeros, Remainder());
|
||||
const int32_t NumToZeroBegin = std::min<int32_t>(NumToZeroEnd, Capacity - WriteIndex);
|
||||
|
||||
memzero(&DestBuffer[WriteIndex], NumToZeroBegin * sizeof(SampleType));
|
||||
memzero(&DestBuffer[0], (NumToZeroEnd - NumToZeroBegin) * sizeof(SampleType));
|
||||
|
||||
WriteCounter = (WriteIndex + NumToZeroEnd) % Capacity;
|
||||
|
||||
return NumToZeroEnd;
|
||||
}
|
||||
|
||||
// Push a single sample onto this buffer.
|
||||
// Returns false if the buffer is full.
|
||||
bool Push(const SampleType& InElement) {
|
||||
if (Remainder() == 0) {
|
||||
return false;
|
||||
} else {
|
||||
SampleType* DestBuffer = InternalBuffer.data();
|
||||
const uint32_t ReadIndex = ReadCounter;
|
||||
const uint32_t WriteIndex = WriteCounter;
|
||||
|
||||
DestBuffer[WriteIndex] = InElement;
|
||||
|
||||
WriteCounter = (WriteIndex + 1) % Capacity;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool Push(SampleType&& InElement) {
|
||||
if (Remainder() == 0) {
|
||||
return false;
|
||||
} else {
|
||||
SampleType* DestBuffer = InternalBuffer.data();
|
||||
const uint32_t ReadIndex = ReadCounter;
|
||||
const uint32_t WriteIndex = WriteCounter;
|
||||
|
||||
DestBuffer[WriteIndex] = MoveTemp(InElement);
|
||||
|
||||
WriteCounter = (WriteIndex + 1) % Capacity;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Same as Pop(), but does not increment the read counter.
|
||||
int32_t Peek(SampleType* OutBuffer, uint32_t NumSamples) const {
|
||||
const SampleType* SrcBuffer = InternalBuffer.data();
|
||||
const uint32_t ReadIndex = ReadCounter;
|
||||
const uint32_t WriteIndex = WriteCounter;
|
||||
|
||||
int32_t NumToCopy = std::min<int32_t>(NumSamples, Num());
|
||||
|
||||
const int32_t NumRead = std::min<int32_t>(NumToCopy, Capacity - ReadIndex);
|
||||
memcpy(OutBuffer, &SrcBuffer[ReadIndex], NumRead * sizeof(SampleType));
|
||||
|
||||
memcpy(&OutBuffer[NumRead], &SrcBuffer[0], (NumToCopy - NumRead) * sizeof(SampleType));
|
||||
|
||||
check(NumSamples < ((uint32_t)INT32_MAX));
|
||||
|
||||
return NumToCopy;
|
||||
}
|
||||
|
||||
// Peeks a single element.
|
||||
// returns false if the element is empty.
|
||||
bool Peek(SampleType& OutElement) const {
|
||||
if (Num() == 0) {
|
||||
return false;
|
||||
} else {
|
||||
SampleType* SrcBuffer = InternalBuffer.data();
|
||||
const uint32_t ReadIndex = ReadCounter;
|
||||
|
||||
OutElement = SrcBuffer[ReadIndex];
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Pops some amount of samples into this circular buffer.
|
||||
// Returns the amount of samples read.
|
||||
int32_t Pop(SampleType* OutBuffer, uint32_t NumSamples) {
|
||||
int32_t NumSamplesRead = Peek(OutBuffer, NumSamples);
|
||||
check(NumSamples < ((uint32_t)INT32_MAX));
|
||||
|
||||
ReadCounter = (ReadCounter + NumSamplesRead) % Capacity;
|
||||
|
||||
return NumSamplesRead;
|
||||
}
|
||||
|
||||
// Pops some amount of samples into this circular buffer.
|
||||
// Returns the amount of samples read.
|
||||
int32_t Pop(uint32_t NumSamples) {
|
||||
check(NumSamples < ((uint32_t)INT32_MAX));
|
||||
|
||||
int32_t NumSamplesRead = std::min<int32_t>(NumSamples, Num());
|
||||
|
||||
ReadCounter = (ReadCounter + NumSamplesRead) % Capacity;
|
||||
|
||||
return NumSamplesRead;
|
||||
}
|
||||
|
||||
// Pops a single element.
|
||||
// Will assert if the buffer is empty. Please check Num() > 0 before calling.
|
||||
SampleType Pop() {
|
||||
// Calling this when the buffer is empty is considered a fatal error.
|
||||
check(Num() > 0);
|
||||
|
||||
SampleType* SrcBuffer = InternalBuffer.data();
|
||||
const uint32_t ReadIndex = ReadCounter;
|
||||
|
||||
SampleType PoppedValue = MoveTempIfPossible(InternalBuffer[ReadIndex]);
|
||||
ReadCounter = (ReadCounter + 1) % Capacity;
|
||||
return PoppedValue;
|
||||
}
|
||||
|
||||
// When called, seeks the read or write cursor to only retain either the NumSamples latest data
|
||||
// (if bRetainOldestSamples is false) or the NumSamples oldest data (if bRetainOldestSamples is true)
|
||||
// in the buffer. Cannot be used to increase the capacity of this buffer.
|
||||
void SetNum(uint32_t NumSamples, bool bRetainOldestSamples = false) {
|
||||
check(NumSamples < Capacity);
|
||||
|
||||
if (bRetainOldestSamples) {
|
||||
WriteCounter = (ReadCounter + NumSamples) % Capacity;
|
||||
} else {
|
||||
int64_t ReadCounterNum = ((int32_t) WriteCounter) - ((int32_t) NumSamples);
|
||||
if (ReadCounterNum < 0) {
|
||||
ReadCounterNum = Capacity + ReadCounterNum;
|
||||
}
|
||||
|
||||
ReadCounter = ReadCounterNum;
|
||||
}
|
||||
}
|
||||
|
||||
// Get number of samples that can be popped off of the buffer.
|
||||
uint32_t Num() const {
|
||||
const int32_t ReadIndex = ReadCounter;
|
||||
const int32_t WriteIndex = WriteCounter;
|
||||
|
||||
if (WriteIndex >= ReadIndex) {
|
||||
return WriteIndex - ReadIndex;
|
||||
} else {
|
||||
return Capacity - ReadIndex + WriteIndex;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the current capacity of the buffer
|
||||
uint32_t GetCapacity() const {
|
||||
return Capacity;
|
||||
}
|
||||
|
||||
// Get number of samples that can be pushed onto the buffer before it is full.
|
||||
uint32_t Remainder() const {
|
||||
const uint32_t ReadIndex = ReadCounter;
|
||||
const uint32_t WriteIndex = WriteCounter;
|
||||
|
||||
return (Capacity - 1 - WriteIndex + ReadIndex) % Capacity;
|
||||
}
|
||||
};
|
140
core/audio/mixer/channel_interface.cpp
Normal file
140
core/audio/mixer/channel_interface.cpp
Normal file
@ -0,0 +1,140 @@
|
||||
#include "channel_interface.h"
|
||||
|
||||
#include "channel_node.h"
|
||||
|
||||
channel_interface::channel_interface(uint32_t input_channels, uint32_t output_channels) {
|
||||
const uint32_t input_node_num = input_channels / 2;
|
||||
const uint32_t output_node_num = output_channels / 2;
|
||||
null_channel_node* null_node = null_channel_node::get();
|
||||
|
||||
input_headers_.resize(input_channels);
|
||||
output_headers_.resize(output_channels);
|
||||
|
||||
for (int i = 0; i < input_node_num; ++i)
|
||||
{
|
||||
input_channel_nodes.push_back(null_node);
|
||||
set_input_channel(i, null_node);
|
||||
}
|
||||
|
||||
for (int i = 0; i < output_node_num; ++i)
|
||||
{
|
||||
output_channel_nodes.push_back(null_node);
|
||||
set_output_channel(i, null_node);
|
||||
}
|
||||
}
|
||||
|
||||
void channel_interface::set_input_channel(uint32_t node_index, channel_node* node) {
|
||||
if (node_index >= input_channel_nodes.size())
|
||||
return;
|
||||
input_channel_nodes[node_index] = node;
|
||||
|
||||
const auto& node_headers = node->get_channel_headers();
|
||||
|
||||
input_headers_[node_index * 2] = node_headers[0];
|
||||
input_headers_[node_index * 2 + 1] = node_headers[1];
|
||||
// TODO rebuild process node
|
||||
}
|
||||
|
||||
void channel_interface::set_input_channel(const std::vector<channel_node*>& nodes, uint32_t start_index) {
|
||||
const uint32_t input_num = std::min(nodes.size() - start_index, input_channel_nodes.size());
|
||||
for (uint32_t i = 0; i < input_num; ++i)
|
||||
{
|
||||
set_input_channel(i + start_index, nodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void channel_interface::set_output_channel(uint32_t node_index, channel_node* node) {
|
||||
if (node_index >= output_channel_nodes.size())
|
||||
return;
|
||||
output_channel_nodes[node_index] = node;
|
||||
|
||||
const auto& node_headers = node->get_channel_headers();
|
||||
|
||||
output_headers_[node_index * 2] = node_headers[0];
|
||||
output_headers_[node_index * 2 + 1] = node_headers[1];
|
||||
// TODO rebuild process node
|
||||
}
|
||||
|
||||
void channel_interface::set_output_channel(const std::vector<channel_node*>& nodes, uint32_t start_index) {
|
||||
const uint32_t output_num = std::min(nodes.size() - start_index, output_channel_nodes.size());
|
||||
for (uint32_t i = 0; i < output_num; ++i)
|
||||
{
|
||||
set_output_channel(i + start_index, nodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t channel_interface::get_input_node_index(channel_node* node) {
|
||||
for (int i = 0; i < input_channel_nodes.size(); ++i) {
|
||||
if (input_channel_nodes[i] == node)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t channel_interface::get_output_node_index(channel_node* node) {
|
||||
for (int i = 0; i < output_channel_nodes.size(); ++i) {
|
||||
if (output_channel_nodes[i] == node)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void channel_interface::set_input_channel_node_name(uint32_t node_index, uint32_t channel_index,
|
||||
const std::string& name) {
|
||||
input_channel_names_[node_index][channel_index] = name;
|
||||
}
|
||||
|
||||
void channel_interface::set_output_channel_node_name(uint32_t node_index, uint32_t channel_index,
|
||||
const std::string& name) {
|
||||
output_channel_names_[node_index][channel_index] = name;
|
||||
}
|
||||
|
||||
std::map<uint32_t, std::string> channel_interface::get_input_channel_node_name(uint32_t node_index) {
|
||||
return input_channel_names_[node_index];
|
||||
}
|
||||
|
||||
std::map<uint32_t, std::string> channel_interface::get_input_channel_node_name(channel_node* node) {
|
||||
uint32_t index = get_input_node_index(node);
|
||||
if (index == -1)
|
||||
return {};
|
||||
return get_input_channel_node_name(index);
|
||||
}
|
||||
|
||||
std::map<uint32_t, std::string> channel_interface::get_output_channel_node_name(uint32_t node_index) {
|
||||
return output_channel_names_[node_index];
|
||||
}
|
||||
|
||||
std::map<uint32_t, std::string> channel_interface::get_output_channel_node_name(channel_node* node) {
|
||||
uint32_t index = get_output_node_index(node);
|
||||
if (index == -1)
|
||||
return {};
|
||||
return get_output_channel_node_name(index);
|
||||
}
|
||||
|
||||
void channel_interface::remove_track(mixer_track* track) {
|
||||
for (int i = 0; i < input_channel_nodes.size(); ++i) {
|
||||
if (input_channel_nodes[i]->type == channel_node_type::mixer) {
|
||||
auto mixer_node = static_cast<mixer_channel_node*>(input_channel_nodes[i]);
|
||||
if (mixer_node->get_track() == track) {
|
||||
set_input_channel(i, null_channel_node::get());
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < output_channel_nodes.size(); ++i) {
|
||||
if (output_channel_nodes[i]->type == channel_node_type::mixer) {
|
||||
auto mixer_node = static_cast<mixer_channel_node*>(output_channel_nodes[i]);
|
||||
if (mixer_node->get_track() == track) {
|
||||
set_output_channel(i, null_channel_node::get());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mixer_channel_interface::mixer_channel_interface(mixer_track* track) : channel_interface(0, 2), track_(track) {
|
||||
auto* node = new mixer_channel_node(this, track, 0);
|
||||
set_output_channel(0, node);
|
||||
}
|
||||
|
||||
mixer_channel_interface::~mixer_channel_interface() {
|
||||
delete output_channel_nodes[0];
|
||||
}
|
49
core/audio/mixer/channel_interface.h
Normal file
49
core/audio/mixer/channel_interface.h
Normal file
@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
#include <map>
|
||||
|
||||
class mixer_track;
|
||||
class channel_node;
|
||||
|
||||
class channel_interface {
|
||||
public:
|
||||
channel_interface(uint32_t input_channels, uint32_t output_channels);
|
||||
virtual ~channel_interface() = default;
|
||||
|
||||
void set_input_channel(uint32_t node_index, channel_node* node);
|
||||
void set_input_channel(const std::vector<channel_node*>& nodes, uint32_t start_index);
|
||||
void set_output_channel(uint32_t node_index, channel_node* node);
|
||||
void set_output_channel(const std::vector<channel_node*>& nodes, uint32_t start_index);
|
||||
|
||||
uint32_t get_input_node_index(channel_node* node);
|
||||
uint32_t get_output_node_index(channel_node* node);
|
||||
|
||||
float** get_input_headers() { return input_headers_.data(); }
|
||||
float** get_output_headers() { return output_headers_.data(); }
|
||||
|
||||
void set_input_channel_node_name(uint32_t node_index, uint32_t channel_index, const std::string& name);
|
||||
void set_output_channel_node_name(uint32_t node_index, uint32_t channel_index, const std::string& name);
|
||||
std::map<uint32_t, std::string> get_input_channel_node_name(uint32_t node_index);
|
||||
std::map<uint32_t, std::string> get_input_channel_node_name(channel_node* node);
|
||||
std::map<uint32_t, std::string> get_output_channel_node_name(uint32_t node_index);
|
||||
std::map<uint32_t, std::string> get_output_channel_node_name(channel_node* node);
|
||||
|
||||
void remove_track(mixer_track* track);
|
||||
|
||||
std::vector<channel_node*> input_channel_nodes;
|
||||
std::vector<channel_node*> output_channel_nodes;
|
||||
private:
|
||||
std::map<uint32_t, std::map<uint32_t, std::string>> input_channel_names_;
|
||||
std::map<uint32_t, std::map<uint32_t, std::string>> output_channel_names_;
|
||||
std::vector<float*> input_headers_;
|
||||
std::vector<float*> output_headers_;
|
||||
};
|
||||
|
||||
class mixer_channel_interface : public channel_interface {
|
||||
public:
|
||||
explicit mixer_channel_interface(mixer_track* track);
|
||||
~mixer_channel_interface() override;
|
||||
|
||||
[[nodiscard]]mixer_track* get_track() const { return track_; }
|
||||
private:
|
||||
mixer_track* track_;
|
||||
};
|
17
core/audio/mixer/channel_node.cpp
Normal file
17
core/audio/mixer/channel_node.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
#include "channel_node.h"
|
||||
|
||||
#include "mixer.h"
|
||||
#include "mixer_track.h"
|
||||
|
||||
null_channel_node null_channel_node::instance;
|
||||
|
||||
mixer_channel_node::mixer_channel_node(channel_interface* in_owner, mixer_track* in_track, uint32_t in_node_index): channel_node(in_owner, channel_node_type::mixer) {
|
||||
const auto& headers = in_track->buffer.get_headers_vector();
|
||||
|
||||
channel_headers_.push_back(headers[in_node_index * 2]);
|
||||
channel_headers_.push_back(headers[in_node_index * 2 + 1]);
|
||||
}
|
||||
|
||||
const std::vector<float*>& null_channel_node::get_channel_headers() {
|
||||
return g_mixer.zero_track->buffer.get_headers_vector();
|
||||
}
|
47
core/audio/mixer/channel_node.h
Normal file
47
core/audio/mixer/channel_node.h
Normal file
@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
class mixer_track;
|
||||
class channel_interface;
|
||||
|
||||
enum class channel_node_type {
|
||||
unknown,
|
||||
mixer,
|
||||
null,
|
||||
};
|
||||
|
||||
class channel_node {
|
||||
public:
|
||||
virtual ~channel_node() = default;
|
||||
|
||||
channel_node(channel_interface* in_owner, channel_node_type in_type) : owner(in_owner), type(in_type) {}
|
||||
|
||||
virtual const std::vector<float*>& get_channel_headers() = 0;
|
||||
virtual std::string get_name() = 0;
|
||||
|
||||
channel_interface* owner;
|
||||
channel_node_type type;
|
||||
};
|
||||
|
||||
class mixer_channel_node : public channel_node {
|
||||
public:
|
||||
mixer_channel_node(channel_interface* in_owner, mixer_track* in_track, uint32_t in_node_index);
|
||||
|
||||
const std::vector<float*>& get_channel_headers() override { return channel_headers_; }
|
||||
std::string get_name() override { return "MixerChannelNode"; }
|
||||
|
||||
[[nodiscard]] mixer_track* get_track() const { return track_; }
|
||||
private:
|
||||
mixer_track* track_;
|
||||
std::vector<float*> channel_headers_;
|
||||
uint32_t node_index_;
|
||||
};
|
||||
|
||||
class null_channel_node : public channel_node {
|
||||
static null_channel_node instance;
|
||||
public:
|
||||
null_channel_node() : channel_node(nullptr, channel_node_type::null) {}
|
||||
static null_channel_node* get() { return &instance; }
|
||||
std::string get_name() override { return "NullChannelNode"; }
|
||||
|
||||
const std::vector<float*>& get_channel_headers() override;
|
||||
};
|
208
core/audio/mixer/mixer.cpp
Normal file
208
core/audio/mixer/mixer.cpp
Normal file
@ -0,0 +1,208 @@
|
||||
#include "mixer.h"
|
||||
|
||||
#include "channel_interface.h"
|
||||
#include "channel_node.h"
|
||||
#include "mixer_track.h"
|
||||
#include "audio/plugin_host/plugin_host.h"
|
||||
#include "audio/plugin_host/plugin_host_manager.h"
|
||||
#include "thread_message/thread_message_hubs.h"
|
||||
#include "taskflow/taskflow.hpp"
|
||||
|
||||
void build_effect_channel_interface(mixer_track* track, const channel_interface* interface, std::map<mixer_track*, int32_t>& processed_tracks) {
|
||||
int32_t& track_current_layer = processed_tracks[track];
|
||||
auto& input_channel_nodes = interface->input_channel_nodes;
|
||||
auto& output_channel_nodes = interface->output_channel_nodes;
|
||||
// 如果这个效果器需要从其他轨道输入,那么目标轨道的深度就是这个轨道的深度+1
|
||||
for (int i = 1; i < input_channel_nodes.size(); ++i)
|
||||
{
|
||||
channel_node* node = input_channel_nodes[i];
|
||||
if (node->type != channel_node_type::mixer)
|
||||
continue;
|
||||
const mixer_channel_node* mixer_node = static_cast<mixer_channel_node*>(node);
|
||||
auto* InputTrack = mixer_node->get_track();
|
||||
int32_t& target_mixer_current_layer = processed_tracks[InputTrack];
|
||||
target_mixer_current_layer = std::max(target_mixer_current_layer + 1, track_current_layer + 1);
|
||||
}
|
||||
|
||||
// 如果这个效果器需要输出到其他轨道,那么这个轨道的深度就是目标轨道的深度+1
|
||||
for (int i = 1; i < output_channel_nodes.size(); ++i)
|
||||
{
|
||||
auto* node = output_channel_nodes[i];
|
||||
if (node->type != channel_node_type::mixer)
|
||||
continue;
|
||||
const auto* MixerChannelNode = static_cast<mixer_channel_node*>(node);
|
||||
auto* MixerTrack = MixerChannelNode->get_track();
|
||||
const int32_t& TargetMixerCurrentLayer = processed_tracks[MixerTrack];
|
||||
track_current_layer = std::max(track_current_layer, TargetMixerCurrentLayer + 1);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t build_process_node_internal(mixer_track* track, std::map<mixer_track*, int32_t>& processed_tracks, int32_t layer) {
|
||||
int32_t& track_current_layer = processed_tracks[track];
|
||||
track_current_layer = std::max(track_current_layer, layer);
|
||||
for (const mixer_track_link& child_link: track->children)
|
||||
{
|
||||
mixer_track* ChildTrack = child_link.track;
|
||||
build_process_node_internal(ChildTrack, processed_tracks, layer + 1);
|
||||
for (const plugin_host* effect: ChildTrack->effects)
|
||||
{
|
||||
build_effect_channel_interface(ChildTrack, effect->interface.get(), processed_tracks);
|
||||
}
|
||||
}
|
||||
return ++layer;
|
||||
}
|
||||
|
||||
void build_instrument_process_node(const plugin_host* host, std::map<mixer_track*, int32_t>& processed_tracks) {
|
||||
for (mixer_track* Track : host->owner_tracks)
|
||||
{
|
||||
build_effect_channel_interface(Track, host->interface.get(), processed_tracks);
|
||||
}
|
||||
}
|
||||
|
||||
void mixer::init(singleton_initliazer& initliazer) {
|
||||
singleton_t<mixer>::init(initliazer);
|
||||
zero_track = new dummy_track();
|
||||
zero_track->rename("zero");
|
||||
zero_track->init();
|
||||
|
||||
const auto master = new dummy_track();
|
||||
master->rename("master");
|
||||
master->init();
|
||||
tracks_.push_back(master);
|
||||
}
|
||||
|
||||
void mixer::release() {
|
||||
singleton_t<mixer>::release();
|
||||
for (mixer_track* const track: tracks_) {
|
||||
delete track;
|
||||
}
|
||||
delete zero_track;
|
||||
}
|
||||
|
||||
dummy_track* mixer::create_dummy_track(const std::string& in_name) {
|
||||
auto* track = new dummy_track();
|
||||
track->rename(in_name);
|
||||
track->init();
|
||||
|
||||
mixer_track_link link{};
|
||||
link.track = track;
|
||||
link.send_level = 1.0f;
|
||||
get_master()->children.push_back(link);
|
||||
|
||||
g_audio_thread_hub.push_message([track, this]() {
|
||||
thread_register_track(track);
|
||||
});
|
||||
return track;
|
||||
}
|
||||
|
||||
instrument_track* mixer::create_instrument_track(const std::string& in_name) {
|
||||
auto* track = new instrument_track();
|
||||
track->rename(in_name);
|
||||
track->init();
|
||||
|
||||
mixer_track_link link{};
|
||||
link.track = track;
|
||||
link.send_level = 1.0f;
|
||||
get_master()->children.push_back(link);
|
||||
// register track
|
||||
g_audio_thread_hub.push_message([track, this] {
|
||||
thread_register_track(track);
|
||||
});
|
||||
return track;
|
||||
}
|
||||
|
||||
void mixer::remove_track(mixer_track* track) {
|
||||
g_audio_thread_hub.push_message([track, this] {
|
||||
thread_register_track(track);
|
||||
});
|
||||
}
|
||||
|
||||
void mixer::process(uint32_t in_frames) {
|
||||
tf::Executor executor;
|
||||
tf::Taskflow taskflow;
|
||||
|
||||
tf::Task previous_task = taskflow.emplace([] {});
|
||||
for (const auto& order: layer_order_) {
|
||||
for (const auto& layer = layer_tracks_[order]; const auto& track: layer) {
|
||||
taskflow.emplace([track, in_frames] {
|
||||
track->process(in_frames);
|
||||
}).succeed(previous_task);
|
||||
}
|
||||
tf::Task new_layer = taskflow.emplace([] {});
|
||||
new_layer.succeed(previous_task);
|
||||
previous_task = new_layer;
|
||||
}
|
||||
executor.run(taskflow).wait();
|
||||
post_process(in_frames);
|
||||
}
|
||||
|
||||
void mixer::reset() {
|
||||
for (const auto track: tracks_) {
|
||||
track->buffer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void mixer::build_process_node() {
|
||||
if (tracks_.empty())
|
||||
return;
|
||||
std::map<mixer_track*, int32_t> processed_tracks;
|
||||
auto* master = get_master();
|
||||
build_process_node_internal(master, processed_tracks, 0);
|
||||
const auto& instruments = g_plugin_host_manager.get_plugin_hosts();
|
||||
for (const plugin_host* instrument: instruments) {
|
||||
build_instrument_process_node(instrument, processed_tracks);
|
||||
}
|
||||
layer_tracks_.clear();
|
||||
for (const auto& pair: processed_tracks) {
|
||||
layer_tracks_[pair.second].push_back(pair.first);
|
||||
}
|
||||
|
||||
layer_order_.clear();
|
||||
for (const auto& key: layer_tracks_ | std::views::keys) {
|
||||
layer_order_.push_back(key);
|
||||
}
|
||||
std::ranges::sort(layer_order_);
|
||||
}
|
||||
|
||||
void mixer::request_build_process_node() {
|
||||
g_audio_thread_hub.push_message([this] {
|
||||
build_process_node();
|
||||
});
|
||||
}
|
||||
|
||||
void post_process_internal(mixer_track* track, uint32_t in_frames, std::vector<mixer_track*>& processed_tracks) {
|
||||
if (std::ranges::find(processed_tracks, track) != processed_tracks.end())
|
||||
return;
|
||||
for (const auto& link: track->children) {
|
||||
post_process_internal(link.track, in_frames, processed_tracks);
|
||||
track->buffer.mix(link.track->buffer, link.send_level);
|
||||
}
|
||||
|
||||
// CalculatePeak ?
|
||||
processed_tracks.push_back(track);
|
||||
}
|
||||
|
||||
void mixer::post_process(uint32_t in_frames) const {
|
||||
const auto master = get_master();
|
||||
std::vector<mixer_track*> processed_tracks;
|
||||
post_process_internal(master, in_frames, processed_tracks);
|
||||
}
|
||||
|
||||
void mixer::thread_register_track(mixer_track* track) {
|
||||
tracks_.push_back(track);
|
||||
build_process_node();
|
||||
}
|
||||
|
||||
void mixer::thread_remove_track(mixer_track* track) {
|
||||
if (const auto it = std::ranges::find(tracks_, track); it != tracks_.end()) {
|
||||
tracks_.erase(it);
|
||||
}
|
||||
get_master()->remove_child(track);
|
||||
build_process_node();
|
||||
on_remove_track.broadcast(track);
|
||||
|
||||
g_main_thread_hub.push_message([track, this]() {
|
||||
on_remove_track_main_thread.broadcast(track);
|
||||
delete track;
|
||||
});
|
||||
}
|
45
core/audio/mixer/mixer.h
Normal file
45
core/audio/mixer/mixer.h
Normal file
@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
#include "misc/delegates.h"
|
||||
#include "misc/singleton/singleton.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
class channel_interface;
|
||||
class instrument_track;
|
||||
class mixer_track;
|
||||
class dummy_track;
|
||||
|
||||
class mixer : public singleton_t<mixer> {
|
||||
typedef std::pair<int32_t, std::vector<mixer_track*>> layer_type;
|
||||
|
||||
public:
|
||||
void init(singleton_initliazer& initliazer) override;
|
||||
void release() override;
|
||||
const char* get_name() override { return "mixer"; }
|
||||
|
||||
dummy_track* create_dummy_track(const std::string& in_name);
|
||||
instrument_track* create_instrument_track(const std::string& in_name);
|
||||
void remove_track(mixer_track* track);
|
||||
|
||||
void process(uint32_t in_frames);
|
||||
void reset();
|
||||
|
||||
[[nodiscard]] dummy_track* get_master() const { return reinterpret_cast<dummy_track*>(tracks_[0]); }
|
||||
void build_process_node();
|
||||
void request_build_process_node();
|
||||
|
||||
dummy_track* zero_track; // 用于没有任何音频输出的通道
|
||||
|
||||
multicast_delegate<mixer_track*> on_remove_track;
|
||||
multicast_delegate<mixer_track*> on_remove_track_main_thread;
|
||||
private:
|
||||
void post_process(uint32_t in_frames) const;
|
||||
void thread_register_track(mixer_track* track);
|
||||
void thread_remove_track(mixer_track* track);
|
||||
|
||||
std::vector<mixer_track*> tracks_;
|
||||
std::map<int32_t, std::vector<mixer_track*>> layer_tracks_;
|
||||
std::vector<int32_t> layer_order_;
|
||||
};
|
||||
|
||||
CORE_API inline mixer g_mixer;
|
45
core/audio/mixer/mixer_track.cpp
Normal file
45
core/audio/mixer/mixer_track.cpp
Normal file
@ -0,0 +1,45 @@
|
||||
#include "mixer_track.h"
|
||||
|
||||
#include "audio/plugin_host/plugin_host.h"
|
||||
|
||||
mixer_track::~mixer_track() {
|
||||
|
||||
}
|
||||
|
||||
void mixer_track::init() {
|
||||
|
||||
}
|
||||
|
||||
void mixer_track::add_effect(plugin_host* in_effect) {
|
||||
in_effect->owner_tracks.push_back(this);
|
||||
|
||||
}
|
||||
|
||||
void mixer_track::remove_effect(plugin_host* in_effect) {
|
||||
|
||||
}
|
||||
|
||||
void mixer_track::add_child(mixer_track* in_child, float in_send_level) {
|
||||
children.push_back({in_child, in_send_level});
|
||||
}
|
||||
|
||||
void mixer_track::remove_child(mixer_track* in_child) {
|
||||
const auto new_end = std::remove_if(children.begin(), children.end(), [in_child](const mixer_track_link& link) {
|
||||
return link.track == in_child;
|
||||
});
|
||||
children.erase(new_end, children.end());
|
||||
}
|
||||
|
||||
void mixer_track::process(uint32_t in_frames) {
|
||||
for (auto effect : effects)
|
||||
effect->process(in_frames);
|
||||
buffer.multiple(volume);
|
||||
}
|
||||
|
||||
void instrument_track::rename(const std::string& in_name) {
|
||||
instrument_->name = in_name;
|
||||
}
|
||||
|
||||
std::string instrument_track::get_name() const {
|
||||
return instrument_->name;
|
||||
}
|
67
core/audio/mixer/mixer_track.h
Normal file
67
core/audio/mixer/mixer_track.h
Normal file
@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
#include "audio/misc/audio_buffer.h"
|
||||
|
||||
class mixer_track;
|
||||
class plugin_host;
|
||||
|
||||
enum class mixer_track_type {
|
||||
unknown,
|
||||
dummy,
|
||||
instrument,
|
||||
};
|
||||
|
||||
struct mixer_track_link {
|
||||
mixer_track* track;
|
||||
float send_level;
|
||||
};
|
||||
|
||||
class mixer_track {
|
||||
public:
|
||||
explicit mixer_track(mixer_track_type in_type) : type_(in_type) {}
|
||||
virtual ~mixer_track();
|
||||
|
||||
void init();
|
||||
void add_effect(plugin_host* in_effect);
|
||||
void remove_effect(plugin_host* in_effect);
|
||||
void add_child(mixer_track* in_child, float in_send_level);
|
||||
void remove_child(mixer_track* in_child);
|
||||
|
||||
void process(uint32_t in_frames);
|
||||
|
||||
virtual void rename(const std::string& in_name) = 0;
|
||||
[[nodiscard]] virtual std::string get_name() const = 0;
|
||||
|
||||
[[nodiscard]] float** get_headers() { return buffer.get_headers(); }
|
||||
|
||||
[[nodiscard]] mixer_track_type get_type() const { return type_; }
|
||||
|
||||
audio_buffer buffer;
|
||||
std::atomic<float> volume = 1.0f;
|
||||
std::vector<plugin_host*> effects{};
|
||||
std::vector<mixer_track_link> children{};
|
||||
private:
|
||||
void add_effect_internal(plugin_host* in_effect);
|
||||
const mixer_track_type type_;
|
||||
};
|
||||
|
||||
class instrument_track : public mixer_track {
|
||||
public:
|
||||
instrument_track() : mixer_track(mixer_track_type::instrument) {}
|
||||
|
||||
void rename(const std::string& in_name) override;
|
||||
[[nodiscard]] std::string get_name() const override;
|
||||
|
||||
[[nodiscard]] plugin_host* get_instrument() const { return instrument_; }
|
||||
private:
|
||||
plugin_host* instrument_;
|
||||
};
|
||||
|
||||
class dummy_track : public mixer_track {
|
||||
public:
|
||||
dummy_track() : mixer_track(mixer_track_type::dummy) {}
|
||||
|
||||
void rename(const std::string& in_name) override { name_ = in_name; }
|
||||
[[nodiscard]] std::string get_name() const override { return name_.c_str(); }
|
||||
private:
|
||||
std::string name_;
|
||||
};
|
4
core/audio/plugin_host/midi_sequencer.cpp
Normal file
4
core/audio/plugin_host/midi_sequencer.cpp
Normal file
@ -0,0 +1,4 @@
|
||||
#include "midi_sequencer.h"
|
||||
|
||||
void midi_sequencer::process(double sample_rate, uint32_t in_frames) {
|
||||
}
|
11
core/audio/plugin_host/midi_sequencer.h
Normal file
11
core/audio/plugin_host/midi_sequencer.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "misc/singleton/singleton.h"
|
||||
|
||||
class midi_sequencer : public singleton_t<midi_sequencer> {
|
||||
public:
|
||||
void process(double sample_rate, uint32_t in_frames);
|
||||
|
||||
const char* get_name() override { return "midi_sequencer"; }
|
||||
};
|
||||
|
||||
CORE_API inline midi_sequencer g_midi_sequencer;
|
1
core/audio/plugin_host/plugin_host.cpp
Normal file
1
core/audio/plugin_host/plugin_host.cpp
Normal file
@ -0,0 +1 @@
|
||||
#include "plugin_host.h"
|
33
core/audio/plugin_host/plugin_host.h
Normal file
33
core/audio/plugin_host/plugin_host.h
Normal file
@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
#include "imgui.h"
|
||||
#include "GLFW/glfw3.h"
|
||||
|
||||
class mixer_track;
|
||||
class channel_interface;
|
||||
|
||||
class plugin_host {
|
||||
public:
|
||||
virtual bool load_plugin(const char* path) = 0;
|
||||
|
||||
virtual void set_enabled(bool enabled) = 0;
|
||||
virtual bool is_enabled() const = 0;
|
||||
|
||||
virtual void on_update_sample_rate(double sample_rate) = 0;
|
||||
virtual void on_update_buffer_size(int buffer_size) = 0;
|
||||
virtual uint32_t get_input_channels() const = 0;
|
||||
virtual uint32_t get_output_channels() const = 0;
|
||||
virtual void update_channel_node_name() {}
|
||||
|
||||
virtual void process(uint32_t frame_num) {}
|
||||
|
||||
virtual void open_editor(GLFWwindow* window) {}
|
||||
virtual void close_editor() {}
|
||||
virtual void idle_editor() {}
|
||||
virtual bool has_editor() const { return false; }
|
||||
virtual ImVec2 get_editor_size() const { return ImVec2(0, 0); }
|
||||
|
||||
std::string name;
|
||||
std::string vendor;
|
||||
std::shared_ptr<channel_interface> interface;
|
||||
std::vector<mixer_track*> owner_tracks;
|
||||
};
|
31
core/audio/plugin_host/plugin_host_manager.cpp
Normal file
31
core/audio/plugin_host/plugin_host_manager.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
#include "plugin_host_manager.h"
|
||||
|
||||
#include "taskflow/taskflow.hpp"
|
||||
|
||||
#include "plugin_host.h"
|
||||
#include "audio/mixer/channel_interface.h"
|
||||
#include "audio/mixer/mixer.h"
|
||||
#include "misc/singleton/singleton_manager.h"
|
||||
|
||||
void plugin_host_manager::init(singleton_initliazer& initliazer) {
|
||||
singleton_t<plugin_host_manager>::init(initliazer);
|
||||
auto* mixer_ptr = initliazer.require<mixer>();
|
||||
mixer_ptr->on_remove_track.add_raw(this, &plugin_host_manager::on_mixer_track_removed);
|
||||
}
|
||||
|
||||
void plugin_host_manager::process(uint32_t in_frames) const {
|
||||
tf::Executor executor;
|
||||
tf::Taskflow taskflow;
|
||||
for (auto host : plugin_hosts_) {
|
||||
taskflow.emplace([host, in_frames] {
|
||||
host->process(in_frames);
|
||||
});
|
||||
}
|
||||
executor.run(taskflow).wait();
|
||||
}
|
||||
|
||||
void plugin_host_manager::on_mixer_track_removed(mixer_track* track) {
|
||||
for (auto host : plugin_hosts_) {
|
||||
host->interface->remove_track(track);
|
||||
}
|
||||
}
|
22
core/audio/plugin_host/plugin_host_manager.h
Normal file
22
core/audio/plugin_host/plugin_host_manager.h
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include "misc/singleton/singleton.h"
|
||||
|
||||
class plugin_host;
|
||||
class mixer_track;
|
||||
|
||||
class plugin_host_manager : public singleton_t<plugin_host_manager> {
|
||||
public:
|
||||
void init(singleton_initliazer& initliazer) override;
|
||||
|
||||
const std::vector<plugin_host*>& get_plugin_hosts() { return plugin_hosts_; }
|
||||
void process(uint32_t in_frames) const;
|
||||
|
||||
const char* get_name() override { return "plugin_host_manager"; }
|
||||
private:
|
||||
|
||||
void on_mixer_track_removed(mixer_track* track);
|
||||
|
||||
std::vector<plugin_host*> plugin_hosts_{};
|
||||
};
|
||||
|
||||
CORE_API inline plugin_host_manager g_plugin_host_manager;
|
@ -7,3 +7,12 @@
|
||||
#define CORE_API __declspec(dllimport)
|
||||
#endif
|
||||
|
||||
#if defined(_DEBUG) || defined(DEBUG)
|
||||
#define checkf(expr, msg, ...) if (!(expr)) { spdlog::error(msg, __VA_ARGS__); throw std::runtime_error(fmt::format(msg, __VA_ARGS__)); }
|
||||
#define check(expr) if (!(expr)) { spdlog::error("Check failed: {}", #expr); throw std::runtime_error(fmt::format("Check failed: {}", #expr)); }
|
||||
#else
|
||||
#define checkf(...)
|
||||
#define check(...)
|
||||
#endif
|
||||
|
||||
|
||||
|
3
core/misc/delegates.cpp
Normal file
3
core/misc/delegates.cpp
Normal file
@ -0,0 +1,3 @@
|
||||
#include "delegates.h"
|
||||
|
||||
unsigned int delegate_handle::CURRENT_ID = 0;
|
1103
core/misc/delegates.h
Normal file
1103
core/misc/delegates.h
Normal file
File diff suppressed because it is too large
Load Diff
43
core/misc/singleton/singleton.h
Normal file
43
core/misc/singleton/singleton.h
Normal file
@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include "singleton_manager.h"
|
||||
|
||||
class singleton_initliazer;
|
||||
|
||||
template<class T>
|
||||
class CORE_API singleton_impl {
|
||||
public:
|
||||
singleton_impl() {
|
||||
instance_ = static_cast<T*>(this);
|
||||
}
|
||||
static void set(T* instance) {
|
||||
instance_ = instance;
|
||||
}
|
||||
static T* get() {
|
||||
return instance_;
|
||||
}
|
||||
static bool exists() {
|
||||
return instance_ != nullptr;
|
||||
}
|
||||
protected:
|
||||
static inline T* instance_ = nullptr;
|
||||
};
|
||||
|
||||
class CORE_API singleton {
|
||||
public:
|
||||
virtual ~singleton() = default;
|
||||
virtual void init(singleton_initliazer& initliazer) {}
|
||||
virtual void post_init() {}
|
||||
virtual void begin_release() {}
|
||||
virtual void release() {}
|
||||
|
||||
virtual const char* get_name() = 0;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class CORE_API singleton_t : public singleton, public singleton_impl<T> {
|
||||
public:
|
||||
singleton_t() {
|
||||
singleton_manager::get()->add(this);
|
||||
}
|
||||
};
|
27
core/misc/singleton/singleton_manager.cpp
Normal file
27
core/misc/singleton/singleton_manager.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
#include "singleton_manager.h"
|
||||
|
||||
#include "singleton.h"
|
||||
|
||||
void singleton_manager::add(singleton* s) {
|
||||
singletons_.push_back(s);
|
||||
}
|
||||
|
||||
void singleton_manager::init() const {
|
||||
singleton_initliazer initliazer;
|
||||
for (const auto s : singletons_) {
|
||||
s->init(initliazer);
|
||||
initliazer.singletons_.push_back(s);
|
||||
}
|
||||
for (const auto s : singletons_) {
|
||||
s->post_init();
|
||||
}
|
||||
}
|
||||
|
||||
void singleton_manager::release() const {
|
||||
for (const auto s : singletons_) {
|
||||
s->begin_release();
|
||||
}
|
||||
for (const auto s : singletons_) {
|
||||
s->release();
|
||||
}
|
||||
}
|
35
core/misc/singleton/singleton_manager.h
Normal file
35
core/misc/singleton/singleton_manager.h
Normal file
@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
class singleton;
|
||||
|
||||
class singleton_initliazer {
|
||||
public:
|
||||
template<class T>
|
||||
T* require() {
|
||||
auto instance = T::get();
|
||||
if (std::find(singletons_.begin(), singletons_.end(), instance) != singletons_.end()) {
|
||||
return instance;
|
||||
}
|
||||
singletons_.push_back(instance);
|
||||
instance->init(*this);
|
||||
return instance;
|
||||
}
|
||||
|
||||
std::vector<singleton*> singletons_{};
|
||||
};
|
||||
|
||||
class CORE_API singleton_manager {
|
||||
public:
|
||||
static singleton_manager* get() {
|
||||
static singleton_manager instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
void add(singleton* s);
|
||||
|
||||
void init() const;
|
||||
void release() const;
|
||||
|
||||
private:
|
||||
std::vector<singleton*> singletons_;
|
||||
};
|
@ -1,7 +1,6 @@
|
||||
#include "renderer.h"
|
||||
|
||||
#include "imgui_impl_glfw.h"
|
||||
#include "render_target.h"
|
||||
|
||||
#include "application/application.h"
|
||||
#include "texture.h"
|
||||
@ -463,9 +462,3 @@ std::shared_ptr<texture> renderer::create_texture(const uint8_t* data, int width
|
||||
out->upload(data, width * height * 4);
|
||||
return out;
|
||||
}
|
||||
|
||||
std::shared_ptr<render_target> renderer::create_render_target(int width, int height, vk::Format format) {
|
||||
auto out = std::make_shared<render_target>();
|
||||
out->init(width, height, format);
|
||||
return out;
|
||||
}
|
||||
|
@ -11,7 +11,8 @@
|
||||
#define APP_USE_VULKAN_DEBUG_REPORT
|
||||
#endif
|
||||
|
||||
class pixel_shader_drawer;
|
||||
#define APP_USE_UNLIMITED_FRAME_RATE
|
||||
|
||||
class render_target;
|
||||
class texture;
|
||||
constexpr ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
|
||||
@ -31,7 +32,7 @@ static void check_vk_result(VkResult err) {
|
||||
if (err == VK_SUCCESS)
|
||||
return;
|
||||
if (err < 0) {
|
||||
spdlog::error("[vulkan] Error: VkResult = {}", err);
|
||||
spdlog::error("[vulkan] Error: VkResult = {}", (int)err);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
@ -51,7 +52,6 @@ public:
|
||||
void end_frame(GLFWwindow* window_handle);
|
||||
|
||||
static CORE_API std::shared_ptr<texture> create_texture(const uint8_t* data, int width, int height, vk::Format format);
|
||||
static CORE_API std::shared_ptr<render_target> create_render_target(int width, int height, vk::Format format);
|
||||
|
||||
void set_vsync(const bool vsync) { vsync_ = vsync; }
|
||||
|
||||
|
1
core/thread_message/thread_message.cpp
Normal file
1
core/thread_message/thread_message.cpp
Normal file
@ -0,0 +1 @@
|
||||
#include "thread_message.h"
|
19
core/thread_message/thread_message.h
Normal file
19
core/thread_message/thread_message.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
class CORE_API thread_message {
|
||||
public:
|
||||
virtual ~thread_message() = default;
|
||||
|
||||
virtual void execute() = 0;
|
||||
};
|
||||
|
||||
class CORE_API lamba_thread_message : public thread_message {
|
||||
public:
|
||||
lamba_thread_message(const std::function<void()>& in_func) : function(in_func) {
|
||||
|
||||
}
|
||||
void execute() override {
|
||||
function();
|
||||
}
|
||||
std::function<void()> function;
|
||||
};
|
12
core/thread_message/thread_message_hub.cpp
Normal file
12
core/thread_message/thread_message_hub.cpp
Normal file
@ -0,0 +1,12 @@
|
||||
#include "thread_message_hub.h"
|
||||
|
||||
#include "thread_message.h"
|
||||
|
||||
void thread_message_hub::process_messages() {
|
||||
while (!messages_.empty()) {
|
||||
thread_message* message = messages_.front();
|
||||
message->execute();
|
||||
mem_pool_.free(message);
|
||||
messages_.pop();
|
||||
}
|
||||
}
|
31
core/thread_message/thread_message_hub.h
Normal file
31
core/thread_message/thread_message_hub.h
Normal file
@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <queue>
|
||||
|
||||
#include "mempool.h"
|
||||
#include "thread_message.h"
|
||||
|
||||
class CORE_API thread_message_hub {
|
||||
public:
|
||||
template<typename T, typename ...Args>
|
||||
void push_message(Args&&... args) {
|
||||
T* message = mem_pool_.alloc<T>(std::forward<Args>(args)...);
|
||||
push_message(message);
|
||||
}
|
||||
template<typename T>
|
||||
void push_message() {
|
||||
T* message = mem_pool_.alloc<T>();
|
||||
push_message(message);
|
||||
}
|
||||
void push_message(thread_message* message) {
|
||||
messages_.push(message);
|
||||
}
|
||||
void push_message(const std::function<void()>& func) {
|
||||
lamba_thread_message* message = mem_pool_.alloc<lamba_thread_message>(func);
|
||||
push_message(message);
|
||||
}
|
||||
void process_messages();
|
||||
private:
|
||||
std::queue<thread_message*> messages_;
|
||||
mempool mem_pool_ = mempool(1024000);
|
||||
};
|
7
core/thread_message/thread_message_hubs.h
Normal file
7
core/thread_message/thread_message_hubs.h
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
#include "thread_message_hub.h"
|
||||
|
||||
inline thread_message_hub g_main_thread_hub;
|
||||
inline thread_message_hub g_audio_thread_hub;
|
||||
|
||||
#define PUSH_MAIN_THREAD(code) g_main_thread_hub.push_message(code);
|
19
third_party/mempool/CMakeLists.txt
vendored
Normal file
19
third_party/mempool/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(mempool)
|
||||
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_CPP_STANDARD 23)
|
||||
|
||||
add_library(${PROJECT_NAME} SHARED
|
||||
mempool/ncx_core.h
|
||||
mempool/ncx_lock.h
|
||||
mempool/ncx_slab.c
|
||||
mempool/ncx_slab.h
|
||||
mempool/unistd.h
|
||||
mempool.h
|
||||
extern.h
|
||||
)
|
||||
include_directories(mempool)
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC mempool ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_precompile_headers(${PROJECT_NAME} PUBLIC extern.h)
|
||||
add_definitions(-Dmempool_EXPORTS)
|
7
third_party/mempool/extern.h
vendored
Normal file
7
third_party/mempool/extern.h
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef mempool_EXPORTS
|
||||
#define MEMPOOL_API __declspec(dllexport)
|
||||
#else
|
||||
#define MEMPOOL_API __declspec(dllimport)
|
||||
#endif
|
46
third_party/mempool/mempool.h
vendored
Normal file
46
third_party/mempool/mempool.h
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
#include "ncx_slab.h"
|
||||
|
||||
class mempool {
|
||||
public:
|
||||
mempool(size_t pool_size = 1024000) {
|
||||
const auto space = (u_char*)malloc(pool_size);
|
||||
|
||||
mem_pool_ = (ncx_slab_pool_t*)space;
|
||||
mem_pool_->addr = space;
|
||||
mem_pool_->min_shift = 3;
|
||||
mem_pool_->end = space + pool_size;
|
||||
|
||||
ncx_slab_init(mem_pool_);
|
||||
}
|
||||
~mempool() {
|
||||
free(mem_pool_->addr);
|
||||
}
|
||||
[[nodiscard]] void* alloc(size_t size) const {
|
||||
return ncx_slab_alloc(mem_pool_, size);
|
||||
}
|
||||
|
||||
template<typename T, typename ...Args>
|
||||
[[nodiscard]] T* alloc(Args&&... args) const {
|
||||
T* out = (T*)alloc(sizeof(T));
|
||||
new (out) T(std::forward<Args>(args)...);
|
||||
return out;
|
||||
}
|
||||
template<typename T>
|
||||
[[nodiscard]] T* alloc() const {
|
||||
T* out = (T*)alloc(sizeof(T));
|
||||
new (out) T();
|
||||
return out;
|
||||
}
|
||||
|
||||
void free(void* p) const {
|
||||
ncx_slab_free(mem_pool_, p);
|
||||
}
|
||||
[[nodiscard]] ncx_slab_stat_t stat() const {
|
||||
ncx_slab_stat_t out;
|
||||
ncx_slab_stat(mem_pool_, &out);
|
||||
return out;
|
||||
}
|
||||
private:
|
||||
ncx_slab_pool_t* mem_pool_ = nullptr;
|
||||
};
|
41
third_party/mempool/mempool/Makefile
vendored
Normal file
41
third_party/mempool/mempool/Makefile
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
CC=gcc
|
||||
CXX=g++
|
||||
INC+=
|
||||
LIB+=
|
||||
|
||||
#判断系统架构(32bit, 64bit)
|
||||
ARCH := $(shell getconf LONG_BIT)
|
||||
ifeq ($(ARCH),64)
|
||||
CFLAGS+= -DNCX_PTR_SIZE=8
|
||||
else
|
||||
CFLAGS+= -DNCX_PTR_SIZE=4
|
||||
endif
|
||||
|
||||
#CFLAGS+= -pipe -O0 -Wall -g3 -ggdb3
|
||||
CFLAGS+= -pipe -O3
|
||||
#定义是否打印日志
|
||||
CFLAGS+= -DLOG_LEVEL=4
|
||||
#是否与malloc类似模拟脏数据
|
||||
#CFLAGS+= -DNCX_DEBUG_MALLOC
|
||||
#是否自动合并碎片
|
||||
CFLAGS+= -DPAGE_MERGE
|
||||
|
||||
TARGET=pool_test
|
||||
ALL:$(TARGET)
|
||||
|
||||
OBJ= ncx_slab.o
|
||||
|
||||
$(TARGET):$(OBJ) main.o
|
||||
$(CC) $(CFLAGS) -o $@ $^ $(LIB)
|
||||
|
||||
pool_bench:$(OBJ) bench.o
|
||||
$(CC) $(CFLAGS) -o $@ $^ $(LIB)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) $(INC) -c -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f *.o
|
||||
rm -f $(TARGET) pool_bench
|
||||
|
||||
install:
|
106
third_party/mempool/mempool/README.md
vendored
Normal file
106
third_party/mempool/mempool/README.md
vendored
Normal file
@ -0,0 +1,106 @@
|
||||
ncx_mempool
|
||||
======================
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
a nginx-based memory pool . <br/>
|
||||
both share-memory and private-memory are supported. it is more efficient than malloc.<br/>
|
||||
it is a very light weight and do not rely on any third-party library.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
1 #include "ncx_slab.h"
|
||||
2
|
||||
3 int main(int argc, char **argv)
|
||||
4 {
|
||||
5 char *p;
|
||||
6 size_t pool_size = 4096000; //4M
|
||||
7 ncx_slab_stat_t stat;
|
||||
8 u_char *space;
|
||||
9 space = (u_char *)malloc(pool_size);
|
||||
10
|
||||
11 ncx_slab_pool_t *sp;
|
||||
12 sp = (ncx_slab_pool_t*) space;
|
||||
13
|
||||
14 sp->addr = space;
|
||||
15 sp->min_shift = 3;
|
||||
16 sp->end = space + pool_size;
|
||||
17
|
||||
18 /*
|
||||
19 * init
|
||||
20 */
|
||||
21 ncx_slab_init(sp);
|
||||
22
|
||||
23 /*
|
||||
24 * alloc
|
||||
25 */
|
||||
26 p = ncx_slab_alloc(sp, 128);
|
||||
27
|
||||
28 /*
|
||||
29 * memory usage api.
|
||||
30 */
|
||||
31 ncx_slab_stat(sp, &stat);
|
||||
32
|
||||
33 /*
|
||||
34 * free
|
||||
35 */
|
||||
36 ncx_slab_free(sp, p);
|
||||
37
|
||||
38 free(space);
|
||||
39
|
||||
40 return 0;
|
||||
41}
|
||||
|
||||
API
|
||||
===
|
||||
**ncx_slab_init(ncx_slab_pool_t *pool)** <br/>
|
||||
**Description**: 初始化内存池结构;
|
||||
|
||||
**ncx_slab_alloc(ncx_slab_pool_t *pool, size_t size)**<br/>
|
||||
**Description**: 内存分配
|
||||
|
||||
**ncx_slab_free(ncx_slab_pool_t *pool, void *p)** <br/>
|
||||
**Description**: 释放内存
|
||||
|
||||
**ncx_slab_stat(ncx_slab_pool_t *pool, ncx_slab_stat_t *stat)**<br/>
|
||||
**Description**: 查看内存池使用情况
|
||||
|
||||
Customization
|
||||
=============
|
||||
正如example所示,内存池内存是由应用层先分配,ncx_mempool是在给定的内存基础上进行分配和回收管理。 <br/>
|
||||
所以内存池本身不关心所管理的内存是私有内存 or 共享内存;
|
||||
|
||||
ncx_lock.h 是锁接口;根据实际需要重定义: <br/>
|
||||
1.多线程共享内存池,可参考pthread_spin_lock <br/>
|
||||
2.多进程共享内存池,可参考nginx的ngx_shmtx.c实现spin lock <br/>
|
||||
3.单进程单线程使用内存池,无锁编程..
|
||||
|
||||
ncx_log.h 是日志接口,根据实际需要重定义.
|
||||
|
||||
ncx_slab.c.orz 是 ncx_slab.c的详细注释,方便理解.
|
||||
|
||||
Author
|
||||
======
|
||||
dcshi(施俊伟) <dcshi@qq.com>
|
||||
|
||||
Copyright and License
|
||||
=====================
|
||||
This module is licensed under the BSD license.
|
||||
Copyright (C) 2013, by dcshi(施俊伟). <dcshi@qq.com>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
|
||||
the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
79
third_party/mempool/mempool/bench.c
vendored
Normal file
79
third_party/mempool/mempool/bench.c
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
#include "ncx_slab.h"
|
||||
#include <sys/time.h>
|
||||
|
||||
uint64_t usTime()
|
||||
{
|
||||
struct timeval tv;
|
||||
uint64_t usec;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
|
||||
usec = ((uint64_t)tv.tv_sec)*1000000LL;
|
||||
usec += tv.tv_usec;
|
||||
|
||||
return usec;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
ncx_slab_pool_t *sp;
|
||||
size_t pool_size;
|
||||
u_char *space;
|
||||
|
||||
pool_size = 4096000; //4M
|
||||
space = (u_char *)malloc(pool_size);
|
||||
sp = (ncx_slab_pool_t*) space;
|
||||
|
||||
sp->addr = space;
|
||||
sp->min_shift = 3;
|
||||
sp->end = space + pool_size;
|
||||
|
||||
ncx_slab_init(sp);
|
||||
|
||||
char *p;
|
||||
uint64_t us_begin;
|
||||
uint64_t us_end;
|
||||
size_t size[] = { 30, 120, 256, 500, 1000, 2000, 3000, 5000};
|
||||
|
||||
printf("size\tncx\tmalloc\tpct\n");
|
||||
|
||||
int i, j;
|
||||
uint64_t t1, t2;
|
||||
for (j = 0; j < sizeof(size)/sizeof(size_t); j++)
|
||||
{
|
||||
size_t s = size[j];
|
||||
printf("%d\t", s);
|
||||
|
||||
//test for ncx_pool
|
||||
us_begin = usTime();
|
||||
for(i = 0; i < 1000000; i++)
|
||||
{
|
||||
p = ncx_slab_alloc(sp, s);
|
||||
|
||||
ncx_slab_free(sp, p);
|
||||
}
|
||||
us_end = usTime();
|
||||
|
||||
t1 = (us_end - us_begin);
|
||||
printf("%llu \t", t1/1000);
|
||||
|
||||
// test for malloc
|
||||
us_begin = usTime();
|
||||
for(i = 0; i < 1000000; i++)
|
||||
{
|
||||
p = (char*)malloc(s);
|
||||
|
||||
free(p);
|
||||
}
|
||||
us_end = usTime();
|
||||
|
||||
t2 = (us_end - us_begin);
|
||||
printf("%llu\t", t2/1000);
|
||||
|
||||
printf("%.2f\n", (double)t1 / (double)t2);
|
||||
}
|
||||
|
||||
free(space);
|
||||
|
||||
return 0;
|
||||
}
|
53
third_party/mempool/mempool/main.c
vendored
Normal file
53
third_party/mempool/mempool/main.c
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
#include "ncx_slab.h"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char *p;
|
||||
size_t pool_size = 4096000; //4M
|
||||
ncx_slab_stat_t stat;
|
||||
u_char *space;
|
||||
space = (u_char *)malloc(pool_size);
|
||||
|
||||
ncx_slab_pool_t *sp;
|
||||
sp = (ncx_slab_pool_t*) space;
|
||||
|
||||
sp->addr = space;
|
||||
sp->min_shift = 3;
|
||||
sp->end = space + pool_size;
|
||||
|
||||
ncx_slab_init(sp);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 1000000; i++)
|
||||
{
|
||||
p = ncx_slab_alloc(sp, 128 + i);
|
||||
if (p == NULL)
|
||||
{
|
||||
printf("%d\n", i);
|
||||
return -1;
|
||||
}
|
||||
ncx_slab_free(sp, p);
|
||||
}
|
||||
ncx_slab_stat(sp, &stat);
|
||||
|
||||
printf("##########################################################################\n");
|
||||
for (i = 0; i < 2500; i++)
|
||||
{
|
||||
p = ncx_slab_alloc(sp, 30 + i);
|
||||
if (p == NULL)
|
||||
{
|
||||
printf("%d\n", i);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (i % 3 == 0)
|
||||
{
|
||||
ncx_slab_free(sp, p);
|
||||
}
|
||||
}
|
||||
ncx_slab_stat(sp, &stat);
|
||||
|
||||
free(space);
|
||||
|
||||
return 0;
|
||||
}
|
25
third_party/mempool/mempool/ncx_core.h
vendored
Normal file
25
third_party/mempool/mempool/ncx_core.h
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
#ifndef _NCX_CORE_H_
|
||||
#define _NCX_CORE_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef unsigned char u_char;
|
||||
typedef uintptr_t ncx_uint_t;
|
||||
typedef intptr_t ncx_int_t;
|
||||
|
||||
#ifndef NCX_ALIGNMENT
|
||||
#define NCX_ALIGNMENT sizeof(unsigned long) /* platform word */
|
||||
#endif
|
||||
|
||||
#define ncx_align(d, a) (((d) + (a - 1)) & ~(a - 1))
|
||||
#define ncx_align_ptr(p, a) \
|
||||
(u_char *) (((uintptr_t) (p) + ((uintptr_t) a - 1)) & ~((uintptr_t) a - 1))
|
||||
|
||||
#define ncx_memzero(buf, n) (void) memset(buf, 0, n)
|
||||
#define ncx_memset(buf, c, n) (void) memset(buf, c, n)
|
||||
|
||||
#endif
|
13
third_party/mempool/mempool/ncx_lock.h
vendored
Normal file
13
third_party/mempool/mempool/ncx_lock.h
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
#ifndef _NCX_LOCK_H_
|
||||
#define _NCX_LOCK_H_
|
||||
|
||||
#define ncx_shmtx_lock(x) { /*void*/ }
|
||||
#define ncx_shmtx_unlock(x) { /*void*/ }
|
||||
|
||||
typedef struct {
|
||||
|
||||
ncx_uint_t spin;
|
||||
|
||||
} ncx_shmtx_t;
|
||||
|
||||
#endif
|
831
third_party/mempool/mempool/ncx_slab.c
vendored
Normal file
831
third_party/mempool/mempool/ncx_slab.c
vendored
Normal file
@ -0,0 +1,831 @@
|
||||
#include "ncx_slab.h"
|
||||
#include <unistd.h>
|
||||
|
||||
#define NCX_SLAB_PAGE_MASK 3
|
||||
#define NCX_SLAB_PAGE 0
|
||||
#define NCX_SLAB_BIG 1
|
||||
#define NCX_SLAB_EXACT 2
|
||||
#define NCX_SLAB_SMALL 3
|
||||
|
||||
#if (NCX_PTR_SIZE == 4)
|
||||
|
||||
#define NCX_SLAB_PAGE_FREE 0
|
||||
#define NCX_SLAB_PAGE_BUSY 0xffffffff
|
||||
#define NCX_SLAB_PAGE_START 0x80000000
|
||||
|
||||
#define NCX_SLAB_SHIFT_MASK 0x0000000f
|
||||
#define NCX_SLAB_MAP_MASK 0xffff0000
|
||||
#define NCX_SLAB_MAP_SHIFT 16
|
||||
|
||||
#define NCX_SLAB_BUSY 0xffffffff
|
||||
|
||||
#else /* (NCX_PTR_SIZE == 8) */
|
||||
|
||||
#define NCX_SLAB_PAGE_FREE 0
|
||||
#define NCX_SLAB_PAGE_BUSY 0xffffffffffffffff
|
||||
#define NCX_SLAB_PAGE_START 0x8000000000000000
|
||||
|
||||
#define NCX_SLAB_SHIFT_MASK 0x000000000000000f
|
||||
#define NCX_SLAB_MAP_MASK 0xffffffff00000000
|
||||
#define NCX_SLAB_MAP_SHIFT 32
|
||||
|
||||
#define NCX_SLAB_BUSY 0xffffffffffffffff
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if (NCX_DEBUG_MALLOC)
|
||||
|
||||
#define ncx_slab_junk(p, size) ncx_memset(p, 0xA5, size)
|
||||
|
||||
#else
|
||||
|
||||
#define ncx_slab_junk(p, size)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static ncx_slab_page_t* ncx_slab_alloc_pages(ncx_slab_pool_t* pool,
|
||||
ncx_uint_t pages);
|
||||
|
||||
static void ncx_slab_free_pages(ncx_slab_pool_t* pool, ncx_slab_page_t* page,
|
||||
ncx_uint_t pages);
|
||||
|
||||
static bool ncx_slab_empty(ncx_slab_pool_t* pool, ncx_slab_page_t* page);
|
||||
|
||||
static ncx_uint_t ncx_slab_max_size;
|
||||
static ncx_uint_t ncx_slab_exact_size;
|
||||
static ncx_uint_t ncx_slab_exact_shift;
|
||||
static ncx_uint_t ncx_pagesize;
|
||||
static ncx_uint_t ncx_pagesize_shift;
|
||||
static ncx_uint_t ncx_real_pages;
|
||||
|
||||
void ncx_slab_init(ncx_slab_pool_t* pool) {
|
||||
u_char* p;
|
||||
size_t size;
|
||||
ncx_uint_t i, n, pages;
|
||||
ncx_slab_page_t* slots;
|
||||
|
||||
/*pagesize*/
|
||||
ncx_pagesize = getpagesize();
|
||||
for (n = ncx_pagesize, ncx_pagesize_shift = 0;
|
||||
n >>= 1; ncx_pagesize_shift++) {
|
||||
/* void */
|
||||
}
|
||||
|
||||
/* STUB */
|
||||
if (ncx_slab_max_size == 0) {
|
||||
ncx_slab_max_size = ncx_pagesize / 2;
|
||||
ncx_slab_exact_size = ncx_pagesize / (8 * sizeof(uintptr_t));
|
||||
for (n = ncx_slab_exact_size; n >>= 1; ncx_slab_exact_shift++) {
|
||||
/* void */
|
||||
}
|
||||
}
|
||||
|
||||
pool->min_size = 1 << pool->min_shift;
|
||||
|
||||
p = (u_char*) pool + sizeof(ncx_slab_pool_t);
|
||||
slots = (ncx_slab_page_t*) p;
|
||||
|
||||
n = ncx_pagesize_shift - pool->min_shift;
|
||||
for (i = 0; i < n; i++) {
|
||||
slots[i].slab = 0;
|
||||
slots[i].next = &slots[i];
|
||||
slots[i].prev = 0;
|
||||
}
|
||||
|
||||
p += n * sizeof(ncx_slab_page_t);
|
||||
|
||||
size = pool->end - p;
|
||||
ncx_slab_junk(p, size);
|
||||
|
||||
pages = (ncx_uint_t) (size / (ncx_pagesize + sizeof(ncx_slab_page_t)));
|
||||
|
||||
ncx_memzero(p, pages * sizeof(ncx_slab_page_t));
|
||||
|
||||
pool->pages = (ncx_slab_page_t*) p;
|
||||
|
||||
pool->free.prev = 0;
|
||||
pool->free.next = (ncx_slab_page_t*) p;
|
||||
|
||||
pool->pages->slab = pages;
|
||||
pool->pages->next = &pool->free;
|
||||
pool->pages->prev = (uintptr_t) &pool->free;
|
||||
|
||||
pool->start = (u_char*)
|
||||
ncx_align_ptr((uintptr_t) p + pages * sizeof(ncx_slab_page_t),
|
||||
ncx_pagesize);
|
||||
|
||||
ncx_real_pages = (pool->end - pool->start) / ncx_pagesize;
|
||||
pool->pages->slab = ncx_real_pages;
|
||||
}
|
||||
|
||||
|
||||
void* ncx_slab_alloc(ncx_slab_pool_t* pool, size_t size) {
|
||||
ncx_shmtx_lock(&pool->mutex);
|
||||
|
||||
void* p = ncx_slab_alloc_locked(pool, size);
|
||||
|
||||
ncx_shmtx_unlock(&pool->mutex);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
void* ncx_slab_alloc_locked(ncx_slab_pool_t* pool, size_t size) {
|
||||
size_t s;
|
||||
uintptr_t p, n, m, mask,* bitmap;
|
||||
ncx_uint_t i, slot, shift, map;
|
||||
ncx_slab_page_t* page,* prev,* slots;
|
||||
|
||||
if (size >= ncx_slab_max_size) {
|
||||
// debug("slab alloc: %zu", size);
|
||||
|
||||
page = ncx_slab_alloc_pages(pool, (size >> ncx_pagesize_shift)
|
||||
+ ((size % ncx_pagesize) ? 1 : 0));
|
||||
if (page) {
|
||||
p = (page - pool->pages) << ncx_pagesize_shift;
|
||||
p += (uintptr_t) pool->start;
|
||||
} else {
|
||||
p = 0;
|
||||
}
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (size > pool->min_size) {
|
||||
shift = 1;
|
||||
for (s = size - 1; s >>= 1; shift++) {
|
||||
/* void */
|
||||
}
|
||||
slot = shift - pool->min_shift;
|
||||
} else {
|
||||
size = pool->min_size;
|
||||
shift = pool->min_shift;
|
||||
slot = 0;
|
||||
}
|
||||
|
||||
slots = (ncx_slab_page_t*) ((u_char*) pool + sizeof(ncx_slab_pool_t));
|
||||
page = slots[slot].next;
|
||||
|
||||
if (page->next != page) {
|
||||
if (shift < ncx_slab_exact_shift) {
|
||||
do {
|
||||
p = (page - pool->pages) << ncx_pagesize_shift;
|
||||
bitmap = (uintptr_t*) (pool->start + p);
|
||||
|
||||
map = (1 << (ncx_pagesize_shift - shift))
|
||||
/ (sizeof(uintptr_t) * 8);
|
||||
|
||||
for (n = 0; n < map; n++) {
|
||||
if (bitmap[n] != NCX_SLAB_BUSY) {
|
||||
for (m = 1, i = 0; m; m <<= 1, i++) {
|
||||
if ((bitmap[n] & m)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bitmap[n] |= m;
|
||||
|
||||
i = ((n * sizeof(uintptr_t) * 8) << shift)
|
||||
+ (i << shift);
|
||||
|
||||
if (bitmap[n] == NCX_SLAB_BUSY) {
|
||||
for (n = n + 1; n < map; n++) {
|
||||
if (bitmap[n] != NCX_SLAB_BUSY) {
|
||||
p = (uintptr_t) bitmap + i;
|
||||
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
prev = (ncx_slab_page_t*)
|
||||
(page->prev & ~NCX_SLAB_PAGE_MASK);
|
||||
prev->next = page->next;
|
||||
page->next->prev = page->prev;
|
||||
|
||||
page->next = NULL;
|
||||
page->prev = NCX_SLAB_SMALL;
|
||||
}
|
||||
|
||||
p = (uintptr_t) bitmap + i;
|
||||
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
page = page->next;
|
||||
} while (page);
|
||||
} else if (shift == ncx_slab_exact_shift) {
|
||||
do {
|
||||
if (page->slab != NCX_SLAB_BUSY) {
|
||||
for (m = 1, i = 0; m; m <<= 1, i++) {
|
||||
if ((page->slab & m)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
page->slab |= m;
|
||||
|
||||
if (page->slab == NCX_SLAB_BUSY) {
|
||||
prev = (ncx_slab_page_t*)
|
||||
(page->prev & ~NCX_SLAB_PAGE_MASK);
|
||||
prev->next = page->next;
|
||||
page->next->prev = page->prev;
|
||||
|
||||
page->next = NULL;
|
||||
page->prev = NCX_SLAB_EXACT;
|
||||
}
|
||||
|
||||
p = (page - pool->pages) << ncx_pagesize_shift;
|
||||
p += i << shift;
|
||||
p += (uintptr_t) pool->start;
|
||||
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
page = page->next;
|
||||
} while (page);
|
||||
} else {
|
||||
/* shift > ncx_slab_exact_shift */
|
||||
|
||||
n = ncx_pagesize_shift - (page->slab & NCX_SLAB_SHIFT_MASK);
|
||||
n = 1 << n;
|
||||
n = ((uintptr_t) 1 << n) - 1;
|
||||
mask = n << NCX_SLAB_MAP_SHIFT;
|
||||
|
||||
do {
|
||||
if ((page->slab & NCX_SLAB_MAP_MASK) != mask) {
|
||||
for (m = (uintptr_t) 1 << NCX_SLAB_MAP_SHIFT, i = 0;
|
||||
m & mask;
|
||||
m <<= 1, i++) {
|
||||
if ((page->slab & m)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
page->slab |= m;
|
||||
|
||||
if ((page->slab & NCX_SLAB_MAP_MASK) == mask) {
|
||||
prev = (ncx_slab_page_t*)
|
||||
(page->prev & ~NCX_SLAB_PAGE_MASK);
|
||||
prev->next = page->next;
|
||||
page->next->prev = page->prev;
|
||||
|
||||
page->next = NULL;
|
||||
page->prev = NCX_SLAB_BIG;
|
||||
}
|
||||
|
||||
p = (page - pool->pages) << ncx_pagesize_shift;
|
||||
p += i << shift;
|
||||
p += (uintptr_t) pool->start;
|
||||
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
page = page->next;
|
||||
} while (page);
|
||||
}
|
||||
}
|
||||
|
||||
page = ncx_slab_alloc_pages(pool, 1);
|
||||
|
||||
if (page) {
|
||||
if (shift < ncx_slab_exact_shift) {
|
||||
p = (page - pool->pages) << ncx_pagesize_shift;
|
||||
bitmap = (uintptr_t*) (pool->start + p);
|
||||
|
||||
s = 1 << shift;
|
||||
n = (1 << (ncx_pagesize_shift - shift)) / 8 / s;
|
||||
|
||||
if (n == 0) {
|
||||
n = 1;
|
||||
}
|
||||
|
||||
bitmap[0] = (2 << n) - 1;
|
||||
|
||||
map = (1 << (ncx_pagesize_shift - shift)) / (sizeof(uintptr_t) * 8);
|
||||
|
||||
for (i = 1; i < map; i++) {
|
||||
bitmap[i] = 0;
|
||||
}
|
||||
|
||||
page->slab = shift;
|
||||
page->next = &slots[slot];
|
||||
page->prev = (uintptr_t) &slots[slot] | NCX_SLAB_SMALL;
|
||||
|
||||
slots[slot].next = page;
|
||||
|
||||
p = ((page - pool->pages) << ncx_pagesize_shift) + s * n;
|
||||
p += (uintptr_t) pool->start;
|
||||
|
||||
goto done;
|
||||
} else if (shift == ncx_slab_exact_shift) {
|
||||
page->slab = 1;
|
||||
page->next = &slots[slot];
|
||||
page->prev = (uintptr_t) &slots[slot] | NCX_SLAB_EXACT;
|
||||
|
||||
slots[slot].next = page;
|
||||
|
||||
p = (page - pool->pages) << ncx_pagesize_shift;
|
||||
p += (uintptr_t) pool->start;
|
||||
|
||||
goto done;
|
||||
} else {
|
||||
/* shift > ncx_slab_exact_shift */
|
||||
|
||||
page->slab = ((uintptr_t) 1 << NCX_SLAB_MAP_SHIFT) | shift;
|
||||
page->next = &slots[slot];
|
||||
page->prev = (uintptr_t) &slots[slot] | NCX_SLAB_BIG;
|
||||
|
||||
slots[slot].next = page;
|
||||
|
||||
p = (page - pool->pages) << ncx_pagesize_shift;
|
||||
p += (uintptr_t) pool->start;
|
||||
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
p = 0;
|
||||
|
||||
done:
|
||||
|
||||
// debug("slab alloc: %p", (void *)p);
|
||||
|
||||
return (void*) p;
|
||||
}
|
||||
|
||||
|
||||
void ncx_slab_free(ncx_slab_pool_t* pool, void* p) {
|
||||
ncx_shmtx_lock(&pool->mutex);
|
||||
|
||||
ncx_slab_free_locked(pool, p);
|
||||
|
||||
ncx_shmtx_unlock(&pool->mutex);
|
||||
}
|
||||
|
||||
|
||||
void ncx_slab_free_locked(ncx_slab_pool_t* pool, void* p) {
|
||||
size_t size;
|
||||
uintptr_t slab, m,* bitmap;
|
||||
ncx_uint_t n, type, slot, shift, map;
|
||||
ncx_slab_page_t* slots,* page;
|
||||
|
||||
// debug("slab free: %p", p);
|
||||
|
||||
if ((u_char*) p < pool->start || (u_char*) p > pool->end) {
|
||||
// error("ncx_slab_free(): outside of pool");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
n = ((u_char*) p - pool->start) >> ncx_pagesize_shift;
|
||||
page = &pool->pages[n];
|
||||
slab = page->slab;
|
||||
type = page->prev & NCX_SLAB_PAGE_MASK;
|
||||
|
||||
switch (type) {
|
||||
case NCX_SLAB_SMALL:
|
||||
|
||||
shift = slab & NCX_SLAB_SHIFT_MASK;
|
||||
size = 1 << shift;
|
||||
|
||||
if ((uintptr_t) p & (size - 1)) {
|
||||
goto wrong_chunk;
|
||||
}
|
||||
|
||||
n = ((uintptr_t) p & (ncx_pagesize - 1)) >> shift;
|
||||
m = (uintptr_t) 1 << (n & (sizeof(uintptr_t) * 8 - 1));
|
||||
n /= (sizeof(uintptr_t) * 8);
|
||||
bitmap = (uintptr_t*) ((uintptr_t) p & ~(ncx_pagesize - 1));
|
||||
|
||||
if (bitmap[n] & m) {
|
||||
if (page->next == NULL) {
|
||||
slots = (ncx_slab_page_t*)
|
||||
((u_char*) pool + sizeof(ncx_slab_pool_t));
|
||||
slot = shift - pool->min_shift;
|
||||
|
||||
page->next = slots[slot].next;
|
||||
slots[slot].next = page;
|
||||
|
||||
page->prev = (uintptr_t) &slots[slot] | NCX_SLAB_SMALL;
|
||||
page->next->prev = (uintptr_t) page | NCX_SLAB_SMALL;
|
||||
}
|
||||
|
||||
bitmap[n] &= ~m;
|
||||
|
||||
n = (1 << (ncx_pagesize_shift - shift)) / 8 / (1 << shift);
|
||||
|
||||
if (n == 0) {
|
||||
n = 1;
|
||||
}
|
||||
|
||||
if (bitmap[0] & ~(((uintptr_t) 1 << n) - 1)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
map = (1 << (ncx_pagesize_shift - shift)) / (sizeof(uintptr_t) * 8);
|
||||
|
||||
for (n = 1; n < map; n++) {
|
||||
if (bitmap[n]) {
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
ncx_slab_free_pages(pool, page, 1);
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
goto chunk_already_free;
|
||||
|
||||
case NCX_SLAB_EXACT:
|
||||
|
||||
m = (uintptr_t) 1 <<
|
||||
(((uintptr_t) p & (ncx_pagesize - 1)) >> ncx_slab_exact_shift);
|
||||
size = ncx_slab_exact_size;
|
||||
|
||||
if ((uintptr_t) p & (size - 1)) {
|
||||
goto wrong_chunk;
|
||||
}
|
||||
|
||||
if (slab & m) {
|
||||
if (slab == NCX_SLAB_BUSY) {
|
||||
slots = (ncx_slab_page_t*)
|
||||
((u_char*) pool + sizeof(ncx_slab_pool_t));
|
||||
slot = ncx_slab_exact_shift - pool->min_shift;
|
||||
|
||||
page->next = slots[slot].next;
|
||||
slots[slot].next = page;
|
||||
|
||||
page->prev = (uintptr_t) &slots[slot] | NCX_SLAB_EXACT;
|
||||
page->next->prev = (uintptr_t) page | NCX_SLAB_EXACT;
|
||||
}
|
||||
|
||||
page->slab &= ~m;
|
||||
|
||||
if (page->slab) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
ncx_slab_free_pages(pool, page, 1);
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
goto chunk_already_free;
|
||||
|
||||
case NCX_SLAB_BIG:
|
||||
|
||||
shift = slab & NCX_SLAB_SHIFT_MASK;
|
||||
size = 1 << shift;
|
||||
|
||||
if ((uintptr_t) p & (size - 1)) {
|
||||
goto wrong_chunk;
|
||||
}
|
||||
|
||||
m = (uintptr_t) 1 << ((((uintptr_t) p & (ncx_pagesize - 1)) >> shift)
|
||||
+ NCX_SLAB_MAP_SHIFT);
|
||||
|
||||
if (slab & m) {
|
||||
if (page->next == NULL) {
|
||||
slots = (ncx_slab_page_t*)
|
||||
((u_char*) pool + sizeof(ncx_slab_pool_t));
|
||||
slot = shift - pool->min_shift;
|
||||
|
||||
page->next = slots[slot].next;
|
||||
slots[slot].next = page;
|
||||
|
||||
page->prev = (uintptr_t) &slots[slot] | NCX_SLAB_BIG;
|
||||
page->next->prev = (uintptr_t) page | NCX_SLAB_BIG;
|
||||
}
|
||||
|
||||
page->slab &= ~m;
|
||||
|
||||
if (page->slab & NCX_SLAB_MAP_MASK) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
ncx_slab_free_pages(pool, page, 1);
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
goto chunk_already_free;
|
||||
|
||||
case NCX_SLAB_PAGE:
|
||||
|
||||
if ((uintptr_t) p & (ncx_pagesize - 1)) {
|
||||
goto wrong_chunk;
|
||||
}
|
||||
|
||||
if (slab == NCX_SLAB_PAGE_FREE) {
|
||||
// alert("ncx_slab_free(): page is already free");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (slab == NCX_SLAB_PAGE_BUSY) {
|
||||
// alert("ncx_slab_free(): pointer to wrong page");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
n = ((u_char*) p - pool->start) >> ncx_pagesize_shift;
|
||||
size = slab & ~NCX_SLAB_PAGE_START;
|
||||
|
||||
ncx_slab_free_pages(pool, &pool->pages[n], size);
|
||||
|
||||
ncx_slab_junk(p, size << ncx_pagesize_shift);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* not reached */
|
||||
|
||||
return;
|
||||
|
||||
done:
|
||||
|
||||
ncx_slab_junk(p, size);
|
||||
|
||||
return;
|
||||
|
||||
wrong_chunk:
|
||||
|
||||
// error("ncx_slab_free(): pointer to wrong chunk");
|
||||
|
||||
goto fail;
|
||||
|
||||
chunk_already_free:
|
||||
|
||||
// error("ncx_slab_free(): chunk is already free");
|
||||
|
||||
fail:
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static ncx_slab_page_t* ncx_slab_alloc_pages(ncx_slab_pool_t* pool, ncx_uint_t pages) {
|
||||
ncx_slab_page_t* page,* p;
|
||||
|
||||
for (page = pool->free.next; page != &pool->free; page = page->next) {
|
||||
if (page->slab >= pages) {
|
||||
if (page->slab > pages) {
|
||||
page[pages].slab = page->slab - pages;
|
||||
page[pages].next = page->next;
|
||||
page[pages].prev = page->prev;
|
||||
|
||||
p = (ncx_slab_page_t*) page->prev;
|
||||
p->next = &page[pages];
|
||||
page->next->prev = (uintptr_t) &page[pages];
|
||||
} else {
|
||||
p = (ncx_slab_page_t*) page->prev;
|
||||
p->next = page->next;
|
||||
page->next->prev = page->prev;
|
||||
}
|
||||
|
||||
page->slab = pages | NCX_SLAB_PAGE_START;
|
||||
page->next = NULL;
|
||||
page->prev = NCX_SLAB_PAGE;
|
||||
|
||||
if (--pages == 0) {
|
||||
return page;
|
||||
}
|
||||
|
||||
for (p = page + 1; pages; pages--) {
|
||||
p->slab = NCX_SLAB_PAGE_BUSY;
|
||||
p->next = NULL;
|
||||
p->prev = NCX_SLAB_PAGE;
|
||||
p++;
|
||||
}
|
||||
|
||||
return page;
|
||||
}
|
||||
}
|
||||
|
||||
// error("ncx_slab_alloc() failed: no memory");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void ncx_slab_free_pages(ncx_slab_pool_t* pool, ncx_slab_page_t* page,
|
||||
ncx_uint_t pages) {
|
||||
ncx_slab_page_t* prev,* next;
|
||||
|
||||
if (pages > 1) {
|
||||
ncx_memzero(&page[1], (pages - 1)* sizeof(ncx_slab_page_t));
|
||||
}
|
||||
|
||||
if (page->next) {
|
||||
prev = (ncx_slab_page_t*) (page->prev & ~NCX_SLAB_PAGE_MASK);
|
||||
prev->next = page->next;
|
||||
page->next->prev = page->prev;
|
||||
}
|
||||
|
||||
page->slab = pages;
|
||||
page->prev = (uintptr_t) &pool->free;
|
||||
page->next = pool->free.next;
|
||||
page->next->prev = (uintptr_t) page;
|
||||
|
||||
pool->free.next = page;
|
||||
|
||||
#ifdef PAGE_MERGE
|
||||
if (pool->pages != page) {
|
||||
prev = page - 1;
|
||||
if (ncx_slab_empty(pool, prev)) {
|
||||
for (; prev >= pool->pages; prev--) {
|
||||
if (prev->slab != 0)
|
||||
{
|
||||
pool->free.next = page->next;
|
||||
page->next->prev = (uintptr_t) &pool->free;
|
||||
|
||||
prev->slab += pages;
|
||||
ncx_memzero(page, sizeof(ncx_slab_page_t));
|
||||
|
||||
page = prev;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((page - pool->pages + page->slab) < ncx_real_pages) {
|
||||
next = page + page->slab;
|
||||
if (ncx_slab_empty(pool, next))
|
||||
{
|
||||
prev = (ncx_slab_page_t *) (next->prev);
|
||||
prev->next = next->next;
|
||||
next->next->prev = next->prev;
|
||||
|
||||
page->slab += next->slab;
|
||||
ncx_memzero(next, sizeof(ncx_slab_page_t));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void ncx_slab_dummy_init(ncx_slab_pool_t* pool) {
|
||||
ncx_uint_t n;
|
||||
|
||||
ncx_pagesize = getpagesize();
|
||||
for (n = ncx_pagesize, ncx_pagesize_shift = 0;
|
||||
n >>= 1; ncx_pagesize_shift++) {
|
||||
/* void */
|
||||
}
|
||||
|
||||
if (ncx_slab_max_size == 0) {
|
||||
ncx_slab_max_size = ncx_pagesize / 2;
|
||||
ncx_slab_exact_size = ncx_pagesize / (8 * sizeof(uintptr_t));
|
||||
for (n = ncx_slab_exact_size; n >>= 1; ncx_slab_exact_shift++) {
|
||||
/* void */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ncx_slab_stat(ncx_slab_pool_t* pool, ncx_slab_stat_t* stat) {
|
||||
uintptr_t m, n, mask, slab;
|
||||
uintptr_t* bitmap;
|
||||
ncx_uint_t i, j, map, type, obj_size;
|
||||
ncx_slab_page_t* page;
|
||||
|
||||
ncx_memzero(stat, sizeof(ncx_slab_stat_t));
|
||||
|
||||
page = pool->pages;
|
||||
stat->pages = (pool->end - pool->start) / ncx_pagesize;;
|
||||
|
||||
for (i = 0; i < stat->pages; i++) {
|
||||
slab = page->slab;
|
||||
type = page->prev & NCX_SLAB_PAGE_MASK;
|
||||
|
||||
switch (type) {
|
||||
case NCX_SLAB_SMALL:
|
||||
|
||||
n = (page - pool->pages) << ncx_pagesize_shift;
|
||||
bitmap = (uintptr_t*) (pool->start + n);
|
||||
|
||||
obj_size = 1 << slab;
|
||||
map = (1 << (ncx_pagesize_shift - slab))
|
||||
/ (sizeof(uintptr_t) * 8);
|
||||
|
||||
for (j = 0; j < map; j++) {
|
||||
for (m = 1; m; m <<= 1) {
|
||||
if ((bitmap[j] & m)) {
|
||||
stat->used_size += obj_size;
|
||||
stat->b_small += obj_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stat->p_small++;
|
||||
|
||||
break;
|
||||
|
||||
case NCX_SLAB_EXACT:
|
||||
|
||||
if (slab == NCX_SLAB_BUSY) {
|
||||
stat->used_size += sizeof(uintptr_t) * 8 * ncx_slab_exact_size;
|
||||
stat->b_exact += sizeof(uintptr_t) * 8 * ncx_slab_exact_size;
|
||||
} else {
|
||||
for (m = 1; m; m <<= 1) {
|
||||
if (slab & m) {
|
||||
stat->used_size += ncx_slab_exact_size;
|
||||
stat->b_exact += ncx_slab_exact_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stat->p_exact++;
|
||||
|
||||
break;
|
||||
|
||||
case NCX_SLAB_BIG:
|
||||
|
||||
j = ncx_pagesize_shift - (slab & NCX_SLAB_SHIFT_MASK);
|
||||
j = 1 << j;
|
||||
j = ((uintptr_t) 1 << j) - 1;
|
||||
mask = j << NCX_SLAB_MAP_SHIFT;
|
||||
obj_size = 1 << (slab & NCX_SLAB_SHIFT_MASK);
|
||||
|
||||
for (m = (uintptr_t) 1 << NCX_SLAB_MAP_SHIFT; m & mask; m <<= 1) {
|
||||
if ((page->slab & m)) {
|
||||
stat->used_size += obj_size;
|
||||
stat->b_big += obj_size;
|
||||
}
|
||||
}
|
||||
|
||||
stat->p_big++;
|
||||
|
||||
break;
|
||||
|
||||
case NCX_SLAB_PAGE:
|
||||
|
||||
if (page->prev == NCX_SLAB_PAGE) {
|
||||
slab = slab & ~NCX_SLAB_PAGE_START;
|
||||
stat->used_size += slab * ncx_pagesize;
|
||||
stat->b_page += slab * ncx_pagesize;
|
||||
stat->p_page += slab;
|
||||
|
||||
i += (slab - 1);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
if (slab > stat->max_free_pages) {
|
||||
stat->max_free_pages = page->slab;
|
||||
}
|
||||
|
||||
stat->free_page += slab;
|
||||
|
||||
i += (slab - 1);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
page = pool->pages + i + 1;
|
||||
}
|
||||
|
||||
stat->pool_size = pool->end - pool->start;
|
||||
stat->used_pct = stat->used_size * 100 / stat->pool_size;
|
||||
|
||||
// info("pool_size : %zu bytes", stat->pool_size);
|
||||
// info("used_size : %zu bytes", stat->used_size);
|
||||
// info("used_pct : %zu%%\n", stat->used_pct);
|
||||
|
||||
// info("total page count : %zu", stat->pages);
|
||||
// info("free page count : %zu\n", stat->free_page);
|
||||
|
||||
// info("small slab use page : %zu,\tbytes : %zu", stat->p_small, stat->b_small);
|
||||
// info("exact slab use page : %zu,\tbytes : %zu", stat->p_exact, stat->b_exact);
|
||||
// info("big slab use page : %zu,\tbytes : %zu", stat->p_big, stat->b_big);
|
||||
// info("page slab use page : %zu,\tbytes : %zu\n", stat->p_page, stat->b_page);
|
||||
|
||||
// info("max free pages : %zu\n", stat->max_free_pages);
|
||||
}
|
||||
|
||||
static bool ncx_slab_empty(ncx_slab_pool_t* pool, ncx_slab_page_t* page) {
|
||||
ncx_slab_page_t* prev;
|
||||
|
||||
if (page->slab == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//page->prev == PAGE | SMALL | EXACT | BIG
|
||||
if (page->next == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
prev = (ncx_slab_page_t*) (page->prev & ~NCX_SLAB_PAGE_MASK);
|
||||
while (prev >= pool->pages) {
|
||||
prev = (ncx_slab_page_t*) (prev->prev & ~NCX_SLAB_PAGE_MASK);
|
||||
};
|
||||
|
||||
if (prev == &pool->free) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
1133
third_party/mempool/mempool/ncx_slab.c.orz
vendored
Normal file
1133
third_party/mempool/mempool/ncx_slab.c.orz
vendored
Normal file
File diff suppressed because it is too large
Load Diff
56
third_party/mempool/mempool/ncx_slab.h
vendored
Normal file
56
third_party/mempool/mempool/ncx_slab.h
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include "ncx_core.h"
|
||||
#include "ncx_lock.h"
|
||||
|
||||
#include "extern.h"
|
||||
|
||||
typedef struct ncx_slab_page_s ncx_slab_page_t;
|
||||
|
||||
struct ncx_slab_page_s {
|
||||
uintptr_t slab;
|
||||
ncx_slab_page_t *next;
|
||||
uintptr_t prev;
|
||||
};
|
||||
|
||||
|
||||
typedef struct {
|
||||
size_t min_size;
|
||||
size_t min_shift;
|
||||
|
||||
ncx_slab_page_t *pages;
|
||||
ncx_slab_page_t free;
|
||||
|
||||
u_char *start;
|
||||
u_char *end;
|
||||
|
||||
ncx_shmtx_t mutex;
|
||||
|
||||
void *addr;
|
||||
} ncx_slab_pool_t;
|
||||
|
||||
typedef struct {
|
||||
size_t pool_size, used_size, used_pct;
|
||||
size_t pages, free_page;
|
||||
size_t p_small, p_exact, p_big, p_page; /* 四种slab占用的page数 */
|
||||
size_t b_small, b_exact, b_big, b_page; /* 四种slab占用的byte数 */
|
||||
size_t max_free_pages; /* 最大的连续可用page数 */
|
||||
} ncx_slab_stat_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" { // only need to export C interface if
|
||||
// used by C++ source code
|
||||
#endif
|
||||
|
||||
MEMPOOL_API void ncx_slab_init(ncx_slab_pool_t *pool);
|
||||
MEMPOOL_API void* ncx_slab_alloc(ncx_slab_pool_t *pool, size_t size);
|
||||
MEMPOOL_API void* ncx_slab_alloc_locked(ncx_slab_pool_t *pool, size_t size);
|
||||
MEMPOOL_API void ncx_slab_free(ncx_slab_pool_t *pool, void *p);
|
||||
MEMPOOL_API void ncx_slab_free_locked(ncx_slab_pool_t *pool, void *p);
|
||||
|
||||
MEMPOOL_API void ncx_slab_dummy_init(ncx_slab_pool_t *pool);
|
||||
MEMPOOL_API void ncx_slab_stat(ncx_slab_pool_t *pool, ncx_slab_stat_t *stat);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
12
third_party/mempool/mempool/unistd.h
vendored
Normal file
12
third_party/mempool/mempool/unistd.h
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
inline uintptr_t getpagesize() {
|
||||
SYSTEM_INFO systemInfo;
|
||||
GetSystemInfo(&systemInfo);
|
||||
return systemInfo.dwPageSize;
|
||||
}
|
||||
#else
|
||||
#include <unisid.h>
|
||||
#endif
|
2
third_party/spdlog
vendored
2
third_party/spdlog
vendored
@ -1 +1 @@
|
||||
Subproject commit 696db97f672e9082e50e50af315d0f4234c82397
|
||||
Subproject commit 69d412b5260ffdb67b2f18a60f4eb1043035d1cf
|
1
third_party/taskflow
vendored
Submodule
1
third_party/taskflow
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit a8434d095ff60c53583646e80b6c57c688ee2a4f
|
Loading…
x
Reference in New Issue
Block a user