SWDEV-555889 - Support mipmap on rocr (#2082)
* SWDEV-555889 - Support mipmap on rocr Support mipmap in hip-rt on rocr backend. Enable all mipmap tests in Windows. Some other minor improvement. Add some SRD logs that will be removed finally. * Add sampler.mipFilter to fix sampler issues on mipmap in rocr. Fix format issues of view of leveled image and mipmap image in blit kernel in rocr. Enabled disabled mipmap tests. * Rewrite view logic * Set word4.f.PITCH = 0 for mipmap SRD on navi31 to fix unstable test issues. Reset last error in nagative tests. * Remove SRD dump log from hip-rt Let Rocr mipmap log be in condition. * minor format chang * Exclude mipmap tests for mi200+ which don't support mipmap.
このコミットが含まれているのは:
@@ -503,9 +503,10 @@ hsa_status_t hsa_ext_image_destroy_v2(hsa_agent_t agent, hsa_ext_image_t image)
|
||||
hsa_status_t hsa_ext_image_mipmap_array_get_level(hsa_agent_t agent,
|
||||
const hsa_ext_image_t* mipmap_array,
|
||||
uint32_t mip_level,
|
||||
const hsa_ext_image_descriptor_v2_t* image_descriptor,
|
||||
hsa_ext_image_t* level_view) {
|
||||
return rocr::core::Runtime::runtime_singleton_->extensions_.image_api
|
||||
.hsa_ext_image_mipmap_array_get_level_fn(agent, mipmap_array, mip_level,
|
||||
.hsa_ext_image_mipmap_array_get_level_fn(agent, mipmap_array, mip_level, image_descriptor,
|
||||
level_view);
|
||||
}
|
||||
|
||||
|
||||
@@ -266,7 +266,7 @@ hsa_status_t hsa_ext_sampler_create(hsa_agent_t agent,
|
||||
}
|
||||
hsa_ext_sampler_descriptor_v2_t sampler_descriptor_v2 = {
|
||||
sampler_descriptor->coordinate_mode,
|
||||
sampler_descriptor->filter_mode,
|
||||
sampler_descriptor->filter_mode, HSA_EXT_SAMPLER_FILTER_MODE_NONE,
|
||||
{sampler_descriptor->address_mode,
|
||||
sampler_descriptor->address_mode, sampler_descriptor->address_mode}
|
||||
};
|
||||
@@ -566,13 +566,15 @@ hsa_status_t hsa_ext_image_destroy_v2(hsa_agent_t agent, hsa_ext_image_t image)
|
||||
|
||||
// per-level view retrieval implementation
|
||||
hsa_status_t HSA_API hsa_ext_image_mipmap_array_get_level(hsa_agent_t agent,
|
||||
const hsa_ext_image_t* mipmapped_array,
|
||||
uint32_t mip_level,
|
||||
hsa_ext_image_t* level_image_out) {
|
||||
const hsa_ext_image_t* mipmapped_array,
|
||||
uint32_t mip_level,
|
||||
const hsa_ext_image_descriptor_v2_t* image_descriptor,
|
||||
hsa_ext_image_t* level_image_out) {
|
||||
TRY;
|
||||
if (!mipmapped_array || !level_image_out) { return HSA_STATUS_ERROR_INVALID_ARGUMENT; }
|
||||
|
||||
return ImageRuntime::instance()->GetMipmapArrayLevelHandle(agent, *mipmapped_array, mip_level, *level_image_out);
|
||||
return ImageRuntime::instance()->GetMipmapArrayLevelHandle(agent, *mipmapped_array, mip_level,
|
||||
image_descriptor, *level_image_out);
|
||||
|
||||
CATCH;
|
||||
}
|
||||
|
||||
@@ -483,7 +483,17 @@ hsa_status_t ImageManagerAi::PopulateSamplerSrd(Sampler& sampler) const {
|
||||
}
|
||||
word2.bits.XY_MIN_FILTER = word2.bits.XY_MAG_FILTER;
|
||||
word2.bits.Z_FILTER = SQ_TEX_Z_FILTER_NONE;
|
||||
word2.bits.MIP_FILTER = SQ_TEX_MIP_FILTER_NONE;
|
||||
|
||||
switch (sampler_descriptor.mipmap_filter_mode) {
|
||||
case HSA_EXT_SAMPLER_FILTER_MODE_NEAREST:
|
||||
word2.bits.MIP_FILTER = static_cast<int>(SQ_TEX_MIP_FILTER_POINT);
|
||||
break;
|
||||
case HSA_EXT_SAMPLER_FILTER_MODE_LINEAR:
|
||||
word2.bits.MIP_FILTER = static_cast<int>(SQ_TEX_MIP_FILTER_LINEAR);
|
||||
break;
|
||||
default:
|
||||
word2.bits.MIP_FILTER = static_cast<int>(SQ_TEX_MIP_FILTER_NONE);
|
||||
}
|
||||
|
||||
word3.u32All = 0;
|
||||
|
||||
@@ -999,6 +1009,8 @@ hsa_status_t ImageManagerAi::PopulateMipLevelSrd(
|
||||
MipmappedArray& level_view,
|
||||
const MipmappedArray& mipmap_array,
|
||||
uint32_t mip_level) const {
|
||||
// Copy entire parent structure (srd is a fixed array, so it's deep-copied automatically)
|
||||
level_view = mipmap_array;
|
||||
|
||||
// SRD already copied from parent, just modify BASE_LEVEL/LAST_LEVEL fields
|
||||
uint32_t* srd_words = reinterpret_cast<uint32_t*>(level_view.srd);
|
||||
@@ -1010,7 +1022,9 @@ hsa_status_t ImageManagerAi::PopulateMipLevelSrd(
|
||||
word3->f.base_level = mip_level;
|
||||
word3->f.last_level = mip_level;
|
||||
|
||||
debug_print("Set SRD mip selection: BASE_LEVEL=%u, LAST_LEVEL=%u", mip_level, mip_level);
|
||||
if (core::Runtime::runtime_singleton_->flag().image_print_srd()) {
|
||||
debug_print("Set SRD mip selection: BASE_LEVEL=%u, LAST_LEVEL=%u", mip_level, mip_level);
|
||||
}
|
||||
|
||||
return HSA_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -601,7 +601,17 @@ hsa_status_t ImageManagerGfx11::PopulateSamplerSrd(Sampler& sampler) const {
|
||||
}
|
||||
word2.bits.XY_MIN_FILTER = word2.bits.XY_MAG_FILTER;
|
||||
word2.bits.Z_FILTER = SQ_TEX_Z_FILTER_NONE;
|
||||
word2.bits.MIP_FILTER = SQ_TEX_MIP_FILTER_NONE;
|
||||
|
||||
switch (sampler_descriptor.mipmap_filter_mode) {
|
||||
case HSA_EXT_SAMPLER_FILTER_MODE_NEAREST:
|
||||
word2.bits.MIP_FILTER = static_cast<int>(SQ_TEX_MIP_FILTER_POINT);
|
||||
break;
|
||||
case HSA_EXT_SAMPLER_FILTER_MODE_LINEAR:
|
||||
word2.bits.MIP_FILTER = static_cast<int>(SQ_TEX_MIP_FILTER_LINEAR);
|
||||
break;
|
||||
default:
|
||||
word2.bits.MIP_FILTER = static_cast<int>(SQ_TEX_MIP_FILTER_NONE);
|
||||
}
|
||||
|
||||
word3.u32All = 0;
|
||||
|
||||
@@ -924,9 +934,8 @@ hsa_status_t ImageManagerGfx11::PopulateMipmapSrd(MipmappedArray& mipmap) const
|
||||
|
||||
// For 1d, 2d and 2d-msaa in gfx11 this is pitch-1
|
||||
if (!mipmap_array && !mipmap_3d) {
|
||||
word4.f.PITCH = out.pitch - 1;
|
||||
word4.f.PITCH = 0; // mipmap dosesn't support custom pitch, so set it as 0
|
||||
}
|
||||
|
||||
word5.val = 0;
|
||||
word6.val = 0;
|
||||
word7.val = 0;
|
||||
@@ -1199,6 +1208,8 @@ hsa_status_t ImageManagerGfx11::PopulateMipLevelSrd(
|
||||
MipmappedArray& level_view,
|
||||
const MipmappedArray& mipmap_array,
|
||||
uint32_t mip_level) const {
|
||||
// Copy entire parent structure (srd is a fixed array, so it's deep-copied automatically)
|
||||
level_view = mipmap_array;
|
||||
|
||||
// SRD already copied from parent, just modify BASE_LEVEL/LAST_LEVEL fields
|
||||
uint32_t* srd_words = reinterpret_cast<uint32_t*>(level_view.srd);
|
||||
@@ -1210,7 +1221,9 @@ hsa_status_t ImageManagerGfx11::PopulateMipLevelSrd(
|
||||
word3->f.BASE_LEVEL = mip_level;
|
||||
word3->f.LAST_LEVEL = mip_level;
|
||||
|
||||
debug_print("Set SRD mip selection: BASE_LEVEL=%u, LAST_LEVEL=%u", mip_level, mip_level);
|
||||
if (core::Runtime::runtime_singleton_->flag().image_print_srd()) {
|
||||
debug_print("Set SRD mip selection: BASE_LEVEL=%u, LAST_LEVEL=%u", mip_level, mip_level);
|
||||
}
|
||||
|
||||
return HSA_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -637,7 +637,17 @@ hsa_status_t ImageManagerGfx12::PopulateSamplerSrd(Sampler& sampler) const {
|
||||
}
|
||||
word2.bits.XY_MIN_FILTER = word2.bits.XY_MAG_FILTER;
|
||||
word2.bits.Z_FILTER = SQ_TEX_Z_FILTER_NONE;
|
||||
word2.bits.MIP_FILTER = SQ_TEX_MIP_FILTER_NONE;
|
||||
|
||||
switch (sampler_descriptor.mipmap_filter_mode) {
|
||||
case HSA_EXT_SAMPLER_FILTER_MODE_NEAREST:
|
||||
word2.bits.MIP_FILTER = static_cast<int>(SQ_TEX_MIP_FILTER_POINT);
|
||||
break;
|
||||
case HSA_EXT_SAMPLER_FILTER_MODE_LINEAR:
|
||||
word2.bits.MIP_FILTER = static_cast<int>(SQ_TEX_MIP_FILTER_LINEAR);
|
||||
break;
|
||||
default:
|
||||
word2.bits.MIP_FILTER = static_cast<int>(SQ_TEX_MIP_FILTER_NONE);
|
||||
}
|
||||
|
||||
word3.u32All = 0;
|
||||
|
||||
@@ -1051,7 +1061,7 @@ hsa_status_t ImageManagerGfx12::PopulateMipmapSrd(MipmappedArray& mipmap) const
|
||||
|
||||
// For 1d, 2d and 2d-msaa, fields DEPTH+PITCH_MSB encode pitch-1
|
||||
if (!mipmap_array && !mipmap_3d) {
|
||||
uint32_t encPitch = out.pitch - 1;
|
||||
uint32_t encPitch = 0; // mipmap dosesn't support custom pitch, so set it as 0
|
||||
word4.f.DEPTH = encPitch & 0x3fff; // first 14 bits
|
||||
word4.f.PITCH_MSB = (encPitch >> 14) & 0x3; // last 2 bits
|
||||
} else {
|
||||
@@ -1358,6 +1368,8 @@ hsa_status_t ImageManagerGfx12::PopulateMipLevelSrd(
|
||||
MipmappedArray& level_view,
|
||||
const MipmappedArray& mipmap_array,
|
||||
uint32_t mip_level) const {
|
||||
// Copy entire parent structure (srd is a fixed array, so it's deep-copied automatically)
|
||||
level_view = mipmap_array;
|
||||
|
||||
// SRD already copied from parent, just modify BASE_LEVEL/LAST_LEVEL fields
|
||||
uint32_t* srd_words = reinterpret_cast<uint32_t*>(level_view.srd);
|
||||
@@ -1370,7 +1382,9 @@ hsa_status_t ImageManagerGfx12::PopulateMipLevelSrd(
|
||||
word1->f.BASE_LEVEL = mip_level;
|
||||
word3->f.LAST_LEVEL = mip_level;
|
||||
|
||||
debug_print("Set SRD mip selection: BASE_LEVEL=%u, LAST_LEVEL=%u", mip_level, mip_level);
|
||||
if (core::Runtime::runtime_singleton_->flag().image_print_srd()) {
|
||||
debug_print("Set SRD mip selection: BASE_LEVEL=%u, LAST_LEVEL=%u", mip_level, mip_level);
|
||||
}
|
||||
|
||||
return HSA_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -594,7 +594,17 @@ hsa_status_t ImageManagerNv::PopulateSamplerSrd(Sampler& sampler) const {
|
||||
}
|
||||
word2.bits.XY_MIN_FILTER = word2.bits.XY_MAG_FILTER;
|
||||
word2.bits.Z_FILTER = SQ_TEX_Z_FILTER_NONE;
|
||||
word2.bits.MIP_FILTER = SQ_TEX_MIP_FILTER_NONE;
|
||||
|
||||
switch (sampler_descriptor.mipmap_filter_mode) {
|
||||
case HSA_EXT_SAMPLER_FILTER_MODE_NEAREST:
|
||||
word2.bits.MIP_FILTER = static_cast<int>(SQ_TEX_MIP_FILTER_POINT);
|
||||
break;
|
||||
case HSA_EXT_SAMPLER_FILTER_MODE_LINEAR:
|
||||
word2.bits.MIP_FILTER = static_cast<int>(SQ_TEX_MIP_FILTER_LINEAR);
|
||||
break;
|
||||
default:
|
||||
word2.bits.MIP_FILTER = static_cast<int>(SQ_TEX_MIP_FILTER_NONE);
|
||||
}
|
||||
|
||||
word3.u32All = 0;
|
||||
|
||||
@@ -923,7 +933,7 @@ hsa_status_t ImageManagerNv::PopulateMipmapSrd(MipmappedArray& mipmap) const {
|
||||
uint32_t minor_ver = MinorVerFromDevID(chip_id_);
|
||||
// For 1d, 2d and 2d-msaa in gfx1030 and beyond this is pitch-1
|
||||
if ((minor_ver >= 3) && !mipmap_array && !mipmap_3d)
|
||||
word4.f.PITCH = out.pitch - 1;
|
||||
word4.f.PITCH = 0; // mipmap dosesn't support custom pitch, so set it as 0
|
||||
|
||||
word5.val = 0;
|
||||
word5.f.MAX_MIP = mipmap.num_levels - 1;
|
||||
@@ -1119,6 +1129,8 @@ hsa_status_t ImageManagerNv::PopulateMipLevelSrd(
|
||||
MipmappedArray& level_view,
|
||||
const MipmappedArray& mipmap_array,
|
||||
uint32_t mip_level) const {
|
||||
// Copy entire parent structure (srd is a fixed array, so it's deep-copied automatically)
|
||||
level_view = mipmap_array;
|
||||
|
||||
// SRD already copied from parent, just modify BASE_LEVEL/LAST_LEVEL fields
|
||||
uint32_t* srd_words = reinterpret_cast<uint32_t*>(level_view.srd);
|
||||
@@ -1130,7 +1142,9 @@ hsa_status_t ImageManagerNv::PopulateMipLevelSrd(
|
||||
word3->f.BASE_LEVEL = mip_level;
|
||||
word3->f.LAST_LEVEL = mip_level;
|
||||
|
||||
debug_print("Set SRD mip selection: BASE_LEVEL=%u, LAST_LEVEL=%u", mip_level, mip_level);
|
||||
if (core::Runtime::runtime_singleton_->flag().image_print_srd()) {
|
||||
debug_print("Set SRD mip selection: BASE_LEVEL=%u, LAST_LEVEL=%u", mip_level, mip_level);
|
||||
}
|
||||
|
||||
return HSA_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -771,8 +771,6 @@ hsa_status_t ImageRuntime::CreateMipmapArrayHandle(
|
||||
mipmap_array->tile_mode = Image::TileMode::TILED;
|
||||
}
|
||||
|
||||
debug_print("Tile mode = %u (0: LINEAR, 1: TILED)", mipmap_array->tile_mode);
|
||||
|
||||
// Initialize the mipmapped array object
|
||||
mipmap_array->component = component;
|
||||
mipmap_array->data = const_cast<void*>(image_data);
|
||||
@@ -782,15 +780,18 @@ hsa_status_t ImageRuntime::CreateMipmapArrayHandle(
|
||||
mipmap_array->flags = 0;
|
||||
|
||||
manager->PopulateMipmapSrd(*mipmap_array);
|
||||
debug_print("Populating mipmapped array SRD...");
|
||||
if (core::Runtime::runtime_singleton_->flag().image_print_srd())
|
||||
mipmap_array->printSRD();
|
||||
|
||||
manager->printSRDDetailed(mipmap_array->srd);
|
||||
|
||||
// assert(mipmap_array->size == required_size);
|
||||
image_handle.handle = mipmap_array->Convert();
|
||||
debug_print("output handle = %lu", image_handle.handle);
|
||||
|
||||
if (core::Runtime::runtime_singleton_->flag().image_print_srd()) {
|
||||
debug_print("Tile mode = %u (0: LINEAR, 1: TILED)", mipmap_array->tile_mode);
|
||||
debug_print("Populating mipmapped array SRD...");
|
||||
mipmap_array->printSRD();
|
||||
manager->printSRDDetailed(mipmap_array->srd);
|
||||
debug_print("output handle = %lu", image_handle.handle);
|
||||
}
|
||||
|
||||
return HSA_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -809,7 +810,12 @@ hsa_status_t ImageRuntime::DestroyMipmapArrayHandle(
|
||||
|
||||
hsa_status_t ImageRuntime::GetMipmapArrayLevelHandle(
|
||||
hsa_agent_t component, const hsa_ext_image_t& mipmapped_array,
|
||||
uint32_t mip_level, hsa_ext_image_t& level_image_out) {
|
||||
uint32_t mip_level, const hsa_ext_image_descriptor_v2_t* image_descriptor,
|
||||
hsa_ext_image_t& level_image_out) {
|
||||
ImageManager * manager = image_manager(component);
|
||||
if (!manager) {
|
||||
return HSA_STATUS_ERROR_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
level_image_out.handle = 0;
|
||||
|
||||
@@ -837,35 +843,42 @@ hsa_status_t ImageRuntime::GetMipmapArrayLevelHandle(
|
||||
return HSA_STATUS_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
debug_print("Creating mip level %u view for %u level mipmap\n",
|
||||
if (core::Runtime::runtime_singleton_->flag().image_print_srd()) {
|
||||
debug_print("Creating mip level %u view for %u level mipmap\n",
|
||||
mip_level, array->num_levels);
|
||||
}
|
||||
|
||||
// Create a view that references the parent mipmap array
|
||||
MipmappedArray* level_view = MipmappedArray::Create(component);
|
||||
if (!level_view) return HSA_STATUS_ERROR_OUT_OF_RESOURCES;
|
||||
|
||||
// Copy entire parent structure (srd is a fixed array, so it's deep-copied automatically)
|
||||
*level_view = *array;
|
||||
|
||||
// Modify SRD to select only the specific mip level
|
||||
ImageManager* manager = image_manager(component);
|
||||
if (!manager) {
|
||||
MipmappedArray::Destroy(level_view);
|
||||
return HSA_STATUS_ERROR_OUT_OF_RESOURCES;
|
||||
auto format = image_descriptor ? &image_descriptor->format : nullptr;
|
||||
if (format &&
|
||||
(array->desc.format.channel_type != format->channel_type ||
|
||||
array->desc.format.channel_order != format->channel_order)) {
|
||||
MipmappedArray tempArray = *array;
|
||||
tempArray.desc.format.channel_type = format->channel_type;
|
||||
tempArray.desc.format.channel_order = format->channel_order;
|
||||
status = manager->PopulateMipmapSrd(tempArray);
|
||||
if (status == HSA_STATUS_SUCCESS) {
|
||||
status = manager->PopulateMipLevelSrd(*level_view, tempArray, mip_level);
|
||||
}
|
||||
else {
|
||||
debug_print("PopulateMipmapSrd() failed with status %d", status);
|
||||
}
|
||||
}
|
||||
else {
|
||||
status = manager->PopulateMipLevelSrd(*level_view, *array, mip_level);
|
||||
}
|
||||
|
||||
status = manager->PopulateMipLevelSrd(*level_view, *array, mip_level);
|
||||
if (status != HSA_STATUS_SUCCESS) {
|
||||
MipmappedArray::Destroy(level_view);
|
||||
return status;
|
||||
}
|
||||
|
||||
debug_print("Created mip level view using SRD fields");
|
||||
if (core::Runtime::runtime_singleton_->flag().image_print_srd())
|
||||
if (core::Runtime::runtime_singleton_->flag().image_print_srd()) {
|
||||
level_view->printSRD();
|
||||
|
||||
manager->printSRDDetailed(level_view->srd);
|
||||
|
||||
manager->printSRDDetailed(level_view->srd);
|
||||
}
|
||||
// Return handle
|
||||
level_image_out.handle = level_view->Convert();
|
||||
return HSA_STATUS_SUCCESS;
|
||||
|
||||
@@ -171,7 +171,8 @@ class ImageRuntime {
|
||||
/// @brief Get the handle for a specific mipmap level in a mipmapped array.
|
||||
hsa_status_t GetMipmapArrayLevelHandle(
|
||||
hsa_agent_t agent, const hsa_ext_image_t& mipmapped_array,
|
||||
uint32_t mip_level, hsa_ext_image_t& level_image_out);
|
||||
uint32_t mip_level, const hsa_ext_image_descriptor_v2_t* image_descriptor,
|
||||
hsa_ext_image_t& level_image_out);
|
||||
|
||||
ImageManager* image_manager(hsa_agent_t agent) {
|
||||
std::map<uint64_t, ImageManager*>::iterator it = image_managers_.find(agent.handle);
|
||||
|
||||
@@ -223,9 +223,9 @@ private:
|
||||
tile_mode = LINEAR;
|
||||
}
|
||||
|
||||
~MipmappedArray() {}
|
||||
|
||||
public:
|
||||
~MipmappedArray() {}
|
||||
|
||||
/// @brief Create a MipmappedArray.
|
||||
/// Only internal metadata is allocated; image data must be provided by the user.
|
||||
static MipmappedArray* Create(hsa_agent_t agent);
|
||||
|
||||
@@ -1053,6 +1053,7 @@ hsa_status_t HSA_API hsa_ext_image_destroy_v2(hsa_agent_t agent, hsa_ext_image_t
|
||||
hsa_status_t HSA_API hsa_ext_image_mipmap_array_get_level(hsa_agent_t agent,
|
||||
const hsa_ext_image_t* mipmapped_array,
|
||||
uint32_t mip_level,
|
||||
const hsa_ext_image_descriptor_v2_t* image_descriptor,
|
||||
hsa_ext_image_t* level_image_out);
|
||||
|
||||
/**
|
||||
@@ -1381,7 +1382,12 @@ typedef enum {
|
||||
* square block or 2x2x2 cube block around the specified coordinate. The
|
||||
* elements are combined using linear interpolation.
|
||||
*/
|
||||
HSA_EXT_SAMPLER_FILTER_MODE_LINEAR = 1
|
||||
HSA_EXT_SAMPLER_FILTER_MODE_LINEAR = 1,
|
||||
|
||||
/**
|
||||
* None filter. Used for mipmap filter mode of non-mipmap images.
|
||||
*/
|
||||
HSA_EXT_SAMPLER_FILTER_MODE_NONE = 2
|
||||
|
||||
} hsa_ext_sampler_filter_mode_t;
|
||||
|
||||
@@ -1422,10 +1428,15 @@ typedef struct hsa_ext_sampler_descriptor_v2_s {
|
||||
hsa_ext_sampler_coordinate_mode32_t coordinate_mode;
|
||||
|
||||
/**
|
||||
* Sampler filter type describes the type of sampling performed.
|
||||
* Sampler filter type describes the type of sampling performed on regular image.
|
||||
*/
|
||||
hsa_ext_sampler_filter_mode32_t filter_mode;
|
||||
|
||||
/**
|
||||
* Sampler filter type describes the type of sampling performed on mipmap image.
|
||||
*/
|
||||
hsa_ext_sampler_filter_mode32_t mipmap_filter_mode;
|
||||
|
||||
/**
|
||||
* Sampler address mode describes the processing of out-of-range image
|
||||
* coordinates.
|
||||
|
||||
新しいイシューから参照
ユーザーをブロックする